From 64546f8550ae4a902ffd1cf763c06383d494410c Mon Sep 17 00:00:00 2001 From: dborth Date: Sun, 14 Sep 2008 20:40:26 +0000 Subject: [PATCH] May 30, 2008 version by emu_kidid --- Makefile | 183 + Makefile.gc | 183 + src/CheatSearch.cpp | 362 ++ src/CheatSearch.h | 75 + src/Cheats.cpp | 1836 ++++++++++ src/Cheats.h | 55 + src/EEprom.cpp | 214 ++ src/EEprom.h | 38 + src/Flash.cpp | 288 ++ src/Flash.h | 33 + src/GBA.cpp | 4360 ++++++++++++++++++++++++ src/GBA.h | 153 + src/GBAinline.h | 556 +++ src/Gfx.cpp | 47 + src/Gfx.h | 1809 ++++++++++ src/Globals.cpp | 135 + src/Globals.h | 149 + src/Mode0.cpp | 652 ++++ src/Mode1.cpp | 599 ++++ src/Mode2.cpp | 550 +++ src/Mode3.cpp | 463 +++ src/Mode4.cpp | 460 +++ src/Mode5.cpp | 463 +++ src/NLS.h | 62 + src/Port.h | 77 + src/RTC.cpp | 245 ++ src/RTC.h | 31 + src/Sound.cpp | 1463 ++++++++ src/Sound.h | 82 + src/Sram.cpp | 32 + src/Sram.h | 26 + src/System.h | 127 + src/Text.cpp | 180 + src/Text.h | 21 + src/Util.cpp | 1196 +++++++ src/Util.h | 66 + src/admame.cpp | 1033 ++++++ src/agbprint.cpp | 106 + src/agbprint.h | 27 + src/arm-new.h | 7472 +++++++++++++++++++++++++++++++++++++++++ src/armdis.cpp | 809 +++++ src/armdis.h | 33 + src/bilinear.cpp | 432 +++ src/bios.cpp | 1293 +++++++ src/bios.h | 46 + src/elf.cpp | 3248 ++++++++++++++++++ src/elf.h | 311 ++ src/exprNode.h | 69 + src/fileio/sdfileio.c | 110 + src/fileio/sdfileio.h | 33 + src/gb/GB.cpp | 3468 +++++++++++++++++++ src/gb/GB.h | 62 + src/gb/gbCheats.cpp | 498 +++ src/gb/gbCheats.h | 60 + src/gb/gbCodes.h | 1445 ++++++++ src/gb/gbCodesCB.h | 1336 ++++++++ src/gb/gbDis.cpp | 260 ++ src/gb/gbGfx.cpp | 563 ++++ src/gb/gbGlobals.cpp | 54 + src/gb/gbGlobals.h | 68 + src/gb/gbMemory.cpp | 1074 ++++++ src/gb/gbMemory.h | 156 + src/gb/gbPrinter.cpp | 249 ++ src/gb/gbPrinter.h | 20 + src/gb/gbSGB.cpp | 1014 ++++++ src/gb/gbSGB.h | 39 + src/gb/gbSound.cpp | 1065 ++++++ src/gb/gbSound.h | 59 + src/getopt.c | 1061 ++++++ src/getopt.h | 142 + src/getopt1.c | 191 ++ src/interframe.cpp | 643 ++++ src/interp.h | 298 ++ src/memgzio.c | 750 +++++ src/memgzio.h | 21 + src/ngc/gcpad.cpp | 289 ++ src/ngc/gcpad.h | 11 + src/ngc/gx_supp.c | 264 ++ src/ngc/gx_supp.h | 29 + src/ngc/main.c | 1 + src/ngc/menu.cpp | 260 ++ src/ngc/mixer.cpp | 73 + src/ngc/mixer.h | 13 + src/ngc/pal60.h | 85 + src/ngc/tbtime.c | 15 + src/ngc/tbtime.h | 27 + src/ngc/vba172.cpp | 790 +++++ src/ngc/vmmem.cpp | 595 ++++ src/ngc/vmmem.h | 15 + src/pixel.cpp | 157 + src/scanline.cpp | 242 ++ src/simple2x.cpp | 112 + src/thumb.h | 2474 ++++++++++++++ src/unzip.cpp | 1277 +++++++ src/unzip.h | 302 ++ src/zutil.h | 220 ++ 96 files changed, 54140 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.gc create mode 100644 src/CheatSearch.cpp create mode 100644 src/CheatSearch.h create mode 100644 src/Cheats.cpp create mode 100644 src/Cheats.h create mode 100644 src/EEprom.cpp create mode 100644 src/EEprom.h create mode 100644 src/Flash.cpp create mode 100644 src/Flash.h create mode 100644 src/GBA.cpp create mode 100644 src/GBA.h create mode 100644 src/GBAinline.h create mode 100644 src/Gfx.cpp create mode 100644 src/Gfx.h create mode 100644 src/Globals.cpp create mode 100644 src/Globals.h create mode 100644 src/Mode0.cpp create mode 100644 src/Mode1.cpp create mode 100644 src/Mode2.cpp create mode 100644 src/Mode3.cpp create mode 100644 src/Mode4.cpp create mode 100644 src/Mode5.cpp create mode 100644 src/NLS.h create mode 100644 src/Port.h create mode 100644 src/RTC.cpp create mode 100644 src/RTC.h create mode 100644 src/Sound.cpp create mode 100644 src/Sound.h create mode 100644 src/Sram.cpp create mode 100644 src/Sram.h create mode 100644 src/System.h create mode 100644 src/Text.cpp create mode 100644 src/Text.h create mode 100644 src/Util.cpp create mode 100644 src/Util.h create mode 100644 src/admame.cpp create mode 100644 src/agbprint.cpp create mode 100644 src/agbprint.h create mode 100644 src/arm-new.h create mode 100644 src/armdis.cpp create mode 100644 src/armdis.h create mode 100644 src/bilinear.cpp create mode 100644 src/bios.cpp create mode 100644 src/bios.h create mode 100644 src/elf.cpp create mode 100644 src/elf.h create mode 100644 src/exprNode.h create mode 100644 src/fileio/sdfileio.c create mode 100644 src/fileio/sdfileio.h create mode 100644 src/gb/GB.cpp create mode 100644 src/gb/GB.h create mode 100644 src/gb/gbCheats.cpp create mode 100644 src/gb/gbCheats.h create mode 100644 src/gb/gbCodes.h create mode 100644 src/gb/gbCodesCB.h create mode 100644 src/gb/gbDis.cpp create mode 100644 src/gb/gbGfx.cpp create mode 100644 src/gb/gbGlobals.cpp create mode 100644 src/gb/gbGlobals.h create mode 100644 src/gb/gbMemory.cpp create mode 100644 src/gb/gbMemory.h create mode 100644 src/gb/gbPrinter.cpp create mode 100644 src/gb/gbPrinter.h create mode 100644 src/gb/gbSGB.cpp create mode 100644 src/gb/gbSGB.h create mode 100644 src/gb/gbSound.cpp create mode 100644 src/gb/gbSound.h create mode 100644 src/getopt.c create mode 100644 src/getopt.h create mode 100644 src/getopt1.c create mode 100644 src/interframe.cpp create mode 100644 src/interp.h create mode 100644 src/memgzio.c create mode 100644 src/memgzio.h create mode 100644 src/ngc/gcpad.cpp create mode 100644 src/ngc/gcpad.h create mode 100644 src/ngc/gx_supp.c create mode 100644 src/ngc/gx_supp.h create mode 100644 src/ngc/main.c create mode 100644 src/ngc/menu.cpp create mode 100644 src/ngc/mixer.cpp create mode 100644 src/ngc/mixer.h create mode 100644 src/ngc/pal60.h create mode 100644 src/ngc/tbtime.c create mode 100644 src/ngc/tbtime.h create mode 100644 src/ngc/vba172.cpp create mode 100644 src/ngc/vmmem.cpp create mode 100644 src/ngc/vmmem.h create mode 100644 src/pixel.cpp create mode 100644 src/scanline.cpp create mode 100644 src/simple2x.cpp create mode 100644 src/thumb.h create mode 100644 src/unzip.cpp create mode 100644 src/unzip.h create mode 100644 src/zutil.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..814703d --- /dev/null +++ b/Makefile @@ -0,0 +1,183 @@ +#--------------------------------------------------------------------------------- +# Generic makefile for Gamecube projects +# +# Tab stops set to 4 +# | | | | +# 0 1 2 3 +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: + +HAVEDIST := $(wildcard w) + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := vba172 +BUILD := build +SOURCES := src/gb src src/fileio src/ngc +INCLUDES = -I$(DEVKITPRO)/libogc/include -I$(DEVKITPRO)/libfat/libogc/include src/gb src src/fileio src/ngc +LIBPATHS = -L$(DEVKITPRO)/libogc/lib/wii -L$(DEVKITPRO)/libfat/libogc/lib/wii + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +MACHDEP = -DGEKKO -mcpu=750 -meabi -mhard-float +CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) -meabi \ + -DWORDS_BIGENDIAN -DPACKAGE=\"VisualBoyAdvance\" \ + -DVERSION=\"1.7.2\" -DC_CORE -DHAVE_ZUTIL_H \ + -DCHANFFS -DSDL -DWII_BUILD +LDFLAGS = $(MACHDEP) -mrvl -Wl,-Map,$(notdir $@).map -Wl,--cref +PREFIX := powerpc-gekko- + +#export PATH:=/c/devkitPPC_r11/bin:/bin + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with +#--------------------------------------------------------------------------------- +LIBS := -lm -lz -lfat -ldb -lwiiuse -lbte -logc + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- +export OUTPUT := $(CURDIR)/$(TARGET) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +export CC := $(PREFIX)gcc +export CXX := $(PREFIX)g++ +export AR := $(PREFIX)ar +export OBJCOPY := $(PREFIX)objcopy +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) *.elf + +#--------------------------------------------------------------------------------- +run: + psoload $(TARGET).dol + +#--------------------------------------------------------------------------------- +reload: + psoload -r $(TARGET).dol + +#--------------------------------------------------------------------------------- +dist: + @echo Files : $(HAVEDIST) + ifeq ($(strip $(HAVEDIST)),) + rule: + @echo "No file" + else + rule2: A + @echo "Have File" + endif + +bindist: + @[ -f $(TARGET).7z ] || rm $(TARGET).7z 2>/dev/null + @7z a -mx=9 -m0=lzma $(TARGET).7z $(TARGET).dol + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf + @echo "Output ... "$(notdir $@) + @$(OBJCOPY) -O binary $< $@ + +#--------------------------------------------------------------------------------- +$(OUTPUT).elf: $(OFILES) + @echo "Linking ... "$(notdir $@) + @$(LD) $^ $(LDFLAGS) $(LIBPATHS) $(LIBS) -o $@ + +#--------------------------------------------------------------------------------- +# Compile Targets for C/C++ +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +%.o : %.cpp + @echo Compiling ... $(notdir $<) + @$(CXX) -MMD $(CFLAGS) -o $@ -c $< + +#--------------------------------------------------------------------------------- +%.o : %.c + @echo Compiling ... $(notdir $<) + @$(CC) -MMD $(CFLAGS) -o $@ -c $< + +#--------------------------------------------------------------------------------- +%.o : %.S + @echo Compiling ... $(notdir $<) + @$(CC) -MMD $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c $< -o $@ + +#--------------------------------------------------------------------------------- +%.o : %.s + @echo Compiling ... $(notdir $<) + @$(CC) -MMD $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c $< -o $@ + +#--------------------------------------------------------------------------------- +# canned command sequence for binary data +#--------------------------------------------------------------------------------- +define bin2o + cp $(<) $(*).tmp + $(OBJCOPY) -I binary -O elf32-powerpc -B powerpc \ + --rename-section .data=.rodata,readonly,data,contents,alloc \ + --redefine-sym _binary_$*_tmp_start=$*\ + --redefine-sym _binary_$*_tmp_end=$*_end\ + --redefine-sym _binary_$*_tmp_size=$*_size\ + $(*).tmp $(@) + echo "extern const u8" $(*)"[];" > $(*).h + echo "extern const u32" $(*)_size[]";" >> $(*).h + rm $(*).tmp +endef + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/Makefile.gc b/Makefile.gc new file mode 100644 index 0000000..d2650bd --- /dev/null +++ b/Makefile.gc @@ -0,0 +1,183 @@ +#--------------------------------------------------------------------------------- +# Generic makefile for Gamecube projects +# +# Tab stops set to 4 +# | | | | +# 0 1 2 3 +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: + +HAVEDIST := $(wildcard w) + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := vba172 +BUILD := build +SOURCES := src/gb src src/fileio src/ngc +INCLUDES = -I$(DEVKITPRO)/libogc/include -I$(DEVKITPRO)/libfat/libogc/include src/gb src src/fileio src/ngc +LIBPATHS = -L$(DEVKITPRO)/libogc/lib/cube -L$(DEVKITPRO)/libfat/libogc/lib/cube + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +MACHDEP = -DGEKKO -mcpu=750 -meabi -mhard-float +CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) -meabi \ + -DWORDS_BIGENDIAN -DPACKAGE=\"VisualBoyAdvance\" \ + -DVERSION=\"1.7.2\" -DC_CORE -DHAVE_ZUTIL_H \ + -DCHANFFS -DSDL -DGC_BUILD +LDFLAGS = $(MACHDEP) -mogc -Wl,-Map,$(notdir $@).map -Wl,--cref +PREFIX := powerpc-gekko- + +#export PATH:=/c/devkitPPC_r11/bin:/bin + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with +#--------------------------------------------------------------------------------- +LIBS := -logc -lz -lm -lfat -logc -lz -lm + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- +export OUTPUT := $(CURDIR)/$(TARGET) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +export CC := $(PREFIX)gcc +export CXX := $(PREFIX)g++ +export AR := $(PREFIX)ar +export OBJCOPY := $(PREFIX)objcopy +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.gc +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) *.elf + +#--------------------------------------------------------------------------------- +run: + psoload $(TARGET).dol + +#--------------------------------------------------------------------------------- +reload: + psoload -r $(TARGET).dol + +#--------------------------------------------------------------------------------- +dist: + @echo Files : $(HAVEDIST) + ifeq ($(strip $(HAVEDIST)),) + rule: + @echo "No file" + else + rule2: A + @echo "Have File" + endif + +bindist: + @[ -f $(TARGET).7z ] || rm $(TARGET).7z 2>/dev/null + @7z a -mx=9 -m0=lzma $(TARGET).7z $(TARGET).dol + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf + @echo "Output ... "$(notdir $@) + @$(OBJCOPY) -O binary $< $@ + +#--------------------------------------------------------------------------------- +$(OUTPUT).elf: $(OFILES) + @echo "Linking ... "$(notdir $@) + @$(LD) $^ $(LDFLAGS) $(LIBPATHS) $(LIBS) -o $@ + +#--------------------------------------------------------------------------------- +# Compile Targets for C/C++ +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +%.o : %.cpp + @echo Compiling ... $(notdir $<) + @$(CXX) -MMD $(CFLAGS) -o $@ -c $< + +#--------------------------------------------------------------------------------- +%.o : %.c + @echo Compiling ... $(notdir $<) + @$(CC) -MMD $(CFLAGS) -o $@ -c $< + +#--------------------------------------------------------------------------------- +%.o : %.S + @echo Compiling ... $(notdir $<) + @$(CC) -MMD $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c $< -o $@ + +#--------------------------------------------------------------------------------- +%.o : %.s + @echo Compiling ... $(notdir $<) + @$(CC) -MMD $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c $< -o $@ + +#--------------------------------------------------------------------------------- +# canned command sequence for binary data +#--------------------------------------------------------------------------------- +define bin2o + cp $(<) $(*).tmp + $(OBJCOPY) -I binary -O elf32-powerpc -B powerpc \ + --rename-section .data=.rodata,readonly,data,contents,alloc \ + --redefine-sym _binary_$*_tmp_start=$*\ + --redefine-sym _binary_$*_tmp_end=$*_end\ + --redefine-sym _binary_$*_tmp_size=$*_size\ + $(*).tmp $(@) + echo "extern const u8" $(*)"[];" > $(*).h + echo "extern const u32" $(*)_size[]";" >> $(*).h + rm $(*).tmp +endef + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/src/CheatSearch.cpp b/src/CheatSearch.cpp new file mode 100644 index 0000000..da460de --- /dev/null +++ b/src/CheatSearch.cpp @@ -0,0 +1,362 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "CheatSearch.h" + +CheatSearchBlock cheatSearchBlocks[4]; + +CheatSearchData cheatSearchData = { + 0, + cheatSearchBlocks + }; + +static bool cheatSearchEQ(u32 a, u32 b) +{ + return a == b; +} + +static bool cheatSearchNE(u32 a, u32 b) +{ + return a != b; +} + +static bool cheatSearchLT(u32 a, u32 b) +{ + return a < b; +} + +static bool cheatSearchLE(u32 a, u32 b) +{ + return a <= b; +} + +static bool cheatSearchGT(u32 a, u32 b) +{ + return a > b; +} + +static bool cheatSearchGE(u32 a, u32 b) +{ + return a >= b; +} + +static bool cheatSearchSignedEQ(s32 a, s32 b) +{ + return a == b; +} + +static bool cheatSearchSignedNE(s32 a, s32 b) +{ + return a != b; +} + +static bool cheatSearchSignedLT(s32 a, s32 b) +{ + return a < b; +} + +static bool cheatSearchSignedLE(s32 a, s32 b) +{ + return a <= b; +} + +static bool cheatSearchSignedGT(s32 a, s32 b) +{ + return a > b; +} + +static bool cheatSearchSignedGE(s32 a, s32 b) +{ + return a >= b; +} + +static bool (*cheatSearchFunc[])(u32,u32) = { + cheatSearchEQ, + cheatSearchNE, + cheatSearchLT, + cheatSearchLE, + cheatSearchGT, + cheatSearchGE + }; + +static bool (*cheatSearchSignedFunc[])(s32,s32) = { + cheatSearchSignedEQ, + cheatSearchSignedNE, + cheatSearchSignedLT, + cheatSearchSignedLE, + cheatSearchSignedGT, + cheatSearchSignedGE + }; + +void cheatSearchCleanup(CheatSearchData *cs) +{ + int count = cs->count; + + for(int i = 0; i < count; i++) + { + free(cs->blocks[i].saved); + free(cs->blocks[i].bits); + } + cs->count = 0; +} + +void cheatSearchStart(const CheatSearchData *cs) +{ + int count = cs->count; + + for(int i = 0; i < count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + + memset(block->bits, 0xff, block->size >> 3); + memcpy(block->saved, block->data, block->size); + } +} + +s32 cheatSearchSignedRead(u8 *data, int off, int size) +{ + u32 res = data[off++]; + + switch(size) + { + case BITS_8: + res <<= 24; + return ((s32)res) >> 24; + case BITS_16: + res |= ((u32)data[off++])<<8; + res <<= 16; + return ((s32)res) >> 16; + case BITS_32: + res |= ((u32)data[off++])<<8; + res |= ((u32)data[off++])<<16; + res |= ((u32)data[off++])<<24; + return (s32)res; + } + return (s32)res; +} + +u32 cheatSearchRead(u8 *data, int off, int size) +{ + u32 res = data[off++]; + if(size == BITS_16) + res |= ((u32)data[off++])<<8; + else if(size == BITS_32) + { + res |= ((u32)data[off++])<<8; + res |= ((u32)data[off++])<<16; + res |= ((u32)data[off++])<<24; + } + return res; +} + +void cheatSearch(const CheatSearchData *cs, int compare, int size, + bool isSigned) +{ + if(compare < 0 || compare > SEARCH_GE) + return; + int inc = 1; + if(size == BITS_16) + inc = 2; + else if(size == BITS_32) + inc = 4; + + if(isSigned) + { + bool (*func)(s32,s32) = cheatSearchSignedFunc[compare]; + + for(int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + u8 *saved = block->saved; + + for(int j = 0; j < size2; j += inc) + { + if(IS_BIT_SET(bits, j)) + { + s32 a = cheatSearchSignedRead(data, j, size); + s32 b = cheatSearchSignedRead(saved,j, size); + + if(!func(a, b)) + { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } + else + { + bool (*func)(u32,u32) = cheatSearchFunc[compare]; + + for(int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + u8 *saved = block->saved; + + for(int j = 0; j < size2; j += inc) + { + if(IS_BIT_SET(bits, j)) + { + u32 a = cheatSearchRead(data, j, size); + u32 b = cheatSearchRead(saved,j, size); + + if(!func(a, b)) + { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } +} + +void cheatSearchValue(const CheatSearchData *cs, int compare, int size, + bool isSigned, u32 value) +{ + if(compare < 0 || compare > SEARCH_GE) + return; + int inc = 1; + if(size == BITS_16) + inc = 2; + else if(size == BITS_32) + inc = 4; + + if(isSigned) + { + bool (*func)(s32,s32) = cheatSearchSignedFunc[compare]; + + for(int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + + for(int j = 0; j < size2; j += inc) + { + if(IS_BIT_SET(bits, j)) + { + s32 a = cheatSearchSignedRead(data, j, size); + s32 b = (s32)value; + + if(!func(a, b)) + { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } + else + { + bool (*func)(u32,u32) = cheatSearchFunc[compare]; + + for(int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + + for(int j = 0; j < size2; j += inc) + { + if(IS_BIT_SET(bits, j)) + { + u32 a = cheatSearchRead(data, j, size); + + if(!func(a, value)) + { + CLEAR_BIT(bits, j); + if(size == BITS_16) + CLEAR_BIT(bits, j+1); + if(size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } +} + +int cheatSearchGetCount(const CheatSearchData *cs, int size) +{ + int res = 0; + int inc = 1; + if(size == BITS_16) + inc = 2; + else if(size == BITS_32) + inc = 4; + + for(int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + + int size2 = block->size; + u8 *bits = block->bits; + for(int j = 0; j < size2; j += inc) + { + if(IS_BIT_SET(bits, j)) + res++; + } + } + return res; +} + +void cheatSearchUpdateValues(const CheatSearchData *cs) +{ + for(int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + + memcpy(block->saved, block->data, block->size); + } +} + diff --git a/src/CheatSearch.h b/src/CheatSearch.h new file mode 100644 index 0000000..5b833cb --- /dev/null +++ b/src/CheatSearch.h @@ -0,0 +1,75 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_CHEATSEARCH_H +#define VBA_CHEATSEARCH_H + +#include "System.h" + +struct CheatSearchBlock + { + int size; + u32 offset; + u8 *bits; + u8 *data; + u8 *saved; + }; + +struct CheatSearchData + { + int count; + CheatSearchBlock *blocks; + }; + +enum { + SEARCH_EQ, + SEARCH_NE, + SEARCH_LT, + SEARCH_LE, + SEARCH_GT, + SEARCH_GE +}; + +enum { + BITS_8, + BITS_16, + BITS_32 +}; + +#define SET_BIT(bits,off) \ + (bits)[(off) >> 3] |= (1 << ((off) & 7)) + +#define CLEAR_BIT(bits, off) \ + (bits)[(off) >> 3] &= ~(1 << ((off) & 7)) + +#define IS_BIT_SET(bits, off) \ + (bits)[(off) >> 3] & (1 << ((off) & 7)) + +extern CheatSearchData cheatSearchData; +extern void cheatSearchCleanup(CheatSearchData *cs); +extern void cheatSearchStart(const CheatSearchData *cs); +extern void cheatSearch(const CheatSearchData *cs, int compare, int size, + bool isSigned); +extern void cheatSearchValue(const CheatSearchData *cs, int compare, int size, + bool isSigned, u32 value); +extern int cheatSearchGetCount(const CheatSearchData *cs, int size); +extern void cheatSearchUpdateValues(const CheatSearchData *cs); +extern s32 cheatSearchSignedRead(u8 *data, int off, int size); +extern u32 cheatSearchRead(u8 *data, int off, int size); +#endif diff --git a/src/Cheats.cpp b/src/Cheats.cpp new file mode 100644 index 0000000..54df425 --- /dev/null +++ b/src/Cheats.cpp @@ -0,0 +1,1836 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "sdfileio.h" +#include +#include +#include +#include + +#include "GBA.h" +#include "GBAinline.h" +#include "Cheats.h" +#include "Globals.h" +#include "NLS.h" +#include "Util.h" + +/** + * Gameshark code types: + * + * NNNNNNNN 001DC0DE - ID code for the game (game 4 character name) from ROM + * DEADFACE XXXXXXXX - changes decryption seeds + * 0AAAAAAA 000000YY - 8-bit constant write + * 1AAAAAAA 0000YYYY - 16-bit constant write + * 2AAAAAAA YYYYYYYY - 32-bit constant write + * 3AAAAAAA YYYYYYYY - ?? + * 6AAAAAAA 0000YYYY - 16-bit ROM Patch (address >> 1) + * 6AAAAAAA 1000YYYY - 16-bit ROM Patch ? (address >> 1) + * 6AAAAAAA 2000YYYY - 16-bit ROM Patch ? (address >> 1) + * 8A1AAAAA 000000YY - 8-bit button write + * 8A2AAAAA 0000YYYY - 16-bit button write + * 8A3AAAAA YYYYYYYY - 32-bit button write + * 80F00000 0000YYYY - button slow motion + * DAAAAAAA 0000YYYY - if address contains 16-bit value enable next code + * FAAAAAAA 0000YYYY - Master code function + * + * CodeBreaker codes types: + * + * 0000AAAA 000Y - Game CRC (Y are flags: 8 - CRC, 2 - DI) + * 1AAAAAAA YYYY - Master Code function (store address at ((YYYY << 0x16) + * + 0x08000100)) + * 2AAAAAAA YYYY - 16-bit or + * 3AAAAAAA YYYY - 8-bit constant write + * 4AAAAAAA YYYY - Slide code + * XXXXCCCC IIII (C is count and I is address increment, X is value incr.) + * 5AAAAAAA CCCC - Super code (Write bytes to address, CCCC is count) + * BBBBBBBB BBBB + * 6AAAAAAA YYYY - 16-bit and + * 7AAAAAAA YYYY - if address contains 16-bit value enable next code + * 8AAAAAAA YYYY - 16-bit constant write + * 9AAAAAAA YYYY - change decryption (when first code only?) + * AAAAAAAA YYYY - if address does not contain 16-bit value enable next code + * BAAAAAAA YYYY - if 16-bit < YYYY + * CAAAAAAA YYYY - if 16-bit > YYYY + * D0000020 YYYY - if button keys equal value enable next code + * EAAAAAAA YYYY - increase value stored in address + */ +#define UNKNOWN_CODE -1 +#define INT_8_BIT_WRITE 0 +#define INT_16_BIT_WRITE 1 +#define INT_32_BIT_WRITE 2 +#define GSA_16_BIT_ROM_PATCH 3 +#define GSA_8_BIT_GS_WRITE 4 +#define GSA_16_BIT_GS_WRITE 5 +#define GSA_32_BIT_GS_WRITE 6 +#define CBA_IF_KEYS_PRESSED 7 +#define CBA_IF_TRUE 8 +#define CBA_SLIDE_CODE 9 +#define CBA_IF_FALSE 10 +#define CBA_AND 11 +#define GSA_8_BIT_GS_WRITE2 12 +#define GSA_16_BIT_GS_WRITE2 13 +#define GSA_32_BIT_GS_WRITE2 14 +#define GSA_16_BIT_ROM_PATCH2 15 +#define GSA_8_BIT_SLIDE 16 +#define GSA_16_BIT_SLIDE 17 +#define GSA_32_BIT_SLIDE 18 +#define GSA_8_BIT_IF_TRUE 19 +#define GSA_32_BIT_IF_TRUE 20 +#define GSA_8_BIT_IF_FALSE 21 +#define GSA_32_BIT_IF_FALSE 22 +#define GSA_8_BIT_FILL 23 +#define GSA_16_BIT_FILL 24 +#define GSA_8_BIT_IF_TRUE2 25 +#define GSA_16_BIT_IF_TRUE2 26 +#define GSA_32_BIT_IF_TRUE2 27 +#define GSA_8_BIT_IF_FALSE2 28 +#define GSA_16_BIT_IF_FALSE2 29 +#define GSA_32_BIT_IF_FALSE2 30 +#define GSA_SLOWDOWN 31 +#define CBA_ADD 32 +#define CBA_OR 33 +#define CBA_LT 34 +#define CBA_GT 35 +#define CBA_SUPER 36 + +CheatsData cheatsList[100]; +int cheatsNumber = 0; + +u8 cheatsCBASeedBuffer[0x30]; +u32 cheatsCBASeed[4]; +u32 cheatsCBATemporaryValue = 0; +u16 cheatsCBATable[256]; +bool cheatsCBATableGenerated = false; + +u8 cheatsCBACurrentSeed[12] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + +#define CHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9')) + +#define CHEAT_PATCH_ROM_16BIT(a,v) \ + WRITE16LE(((u16 *)&rom[(a) & 0x1ffffff]), v); + +static bool isMultilineWithData(int i) +{ + // we consider it a multiline code if it has more than one line of data + // otherwise, it can still be considered a single code + if(i < cheatsNumber && i >= 0) + switch(cheatsList[i].size) + { + case INT_8_BIT_WRITE: + case INT_16_BIT_WRITE: + case INT_32_BIT_WRITE: + case GSA_16_BIT_ROM_PATCH: + case GSA_8_BIT_GS_WRITE: + case GSA_16_BIT_GS_WRITE: + case GSA_32_BIT_GS_WRITE: + case CBA_AND: + case CBA_IF_KEYS_PRESSED: + case CBA_IF_TRUE: + case CBA_IF_FALSE: + case GSA_8_BIT_IF_TRUE: + case GSA_32_BIT_IF_TRUE: + case GSA_8_BIT_IF_FALSE: + case GSA_32_BIT_IF_FALSE: + case GSA_8_BIT_FILL: + case GSA_16_BIT_FILL: + case GSA_8_BIT_IF_TRUE2: + case GSA_16_BIT_IF_TRUE2: + case GSA_32_BIT_IF_TRUE2: + case GSA_8_BIT_IF_FALSE2: + case GSA_16_BIT_IF_FALSE2: + case GSA_32_BIT_IF_FALSE2: + case GSA_SLOWDOWN: + case CBA_ADD: + case CBA_OR: + return false; + // the codes below have two lines of data + case CBA_SLIDE_CODE: + case GSA_8_BIT_GS_WRITE2: + case GSA_16_BIT_GS_WRITE2: + case GSA_32_BIT_GS_WRITE2: + case GSA_16_BIT_ROM_PATCH2: + case GSA_8_BIT_SLIDE: + case GSA_16_BIT_SLIDE: + case GSA_32_BIT_SLIDE: + case CBA_LT: + case CBA_GT: + case CBA_SUPER: + return true; + } + return false; +} + +static int getCodeLength(int num) +{ + if(num >= cheatsNumber || num < 0) + return 1; + + // this is for all the codes that are true multiline + switch(cheatsList[num].size) + { + case INT_8_BIT_WRITE: + case INT_16_BIT_WRITE: + case INT_32_BIT_WRITE: + case GSA_16_BIT_ROM_PATCH: + case GSA_8_BIT_GS_WRITE: + case GSA_16_BIT_GS_WRITE: + case GSA_32_BIT_GS_WRITE: + case CBA_AND: + case GSA_8_BIT_FILL: + case GSA_16_BIT_FILL: + case GSA_SLOWDOWN: + case CBA_ADD: + case CBA_OR: + return 1; + case CBA_IF_KEYS_PRESSED: + case CBA_IF_TRUE: + case CBA_IF_FALSE: + case CBA_SLIDE_CODE: + case GSA_8_BIT_GS_WRITE2: + case GSA_16_BIT_GS_WRITE2: + case GSA_32_BIT_GS_WRITE2: + case GSA_16_BIT_ROM_PATCH2: + case GSA_8_BIT_SLIDE: + case GSA_16_BIT_SLIDE: + case GSA_32_BIT_SLIDE: + case GSA_8_BIT_IF_TRUE: + case GSA_32_BIT_IF_TRUE: + case GSA_8_BIT_IF_FALSE: + case GSA_32_BIT_IF_FALSE: + case CBA_LT: + case CBA_GT: + return 2; + case GSA_8_BIT_IF_TRUE2: + case GSA_16_BIT_IF_TRUE2: + case GSA_32_BIT_IF_TRUE2: + case GSA_8_BIT_IF_FALSE2: + case GSA_16_BIT_IF_FALSE2: + case GSA_32_BIT_IF_FALSE2: + return 3; + case CBA_SUPER: + return (cheatsList[num].value+5)/6; + } + return 1; +} + +int cheatsCheckKeys(u32 keys, u32 extended) +{ + int ticks = 0; + for(int i = 0; i < cheatsNumber; i++) + { + if(!cheatsList[i].enabled) + { + // make sure we skip other lines in this code + i += getCodeLength(i)-1; + continue; + } + switch(cheatsList[i].size) + { + case INT_8_BIT_WRITE: + CPUWriteByte(cheatsList[i].address, cheatsList[i].value); + break; + case INT_16_BIT_WRITE: + CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value); + break; + case INT_32_BIT_WRITE: + CPUWriteMemory(cheatsList[i].address, cheatsList[i].value); + break; + case GSA_16_BIT_ROM_PATCH: + if((cheatsList[i].status & 1) == 0) + { + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) + { + cheatsList[i].oldValue = CPUReadHalfWord(cheatsList[i].address); + cheatsList[i].status |= 1; + CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, cheatsList[i].value); + } + } + break; + case GSA_8_BIT_GS_WRITE: + if(extended & 4) + { + CPUWriteByte(cheatsList[i].address, cheatsList[i].value); + } + break; + case GSA_16_BIT_GS_WRITE: + if(extended & 4) + { + CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value); + } + break; + case GSA_32_BIT_GS_WRITE: + if(extended & 4) + { + CPUWriteMemory(cheatsList[i].address, cheatsList[i].value); + } + break; + case CBA_IF_KEYS_PRESSED: + { + u16 value = cheatsList[i].value; + u32 addr = cheatsList[i].address; + if((addr & 0x30) == 0x20) + { + if((keys & value) != value) + { + i++; + } + } + else if((addr & 0x30) == 0x10) + { + if((keys & value) == value) + { + i++; + } + } + } + break; + case CBA_IF_TRUE: + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) + { + i++; + } + break; + case CBA_SLIDE_CODE: + { + u32 address = cheatsList[i].address; + u16 value = cheatsList[i].value; + i++; + if(i < cheatsNumber) + { + int count = (cheatsList[i].address & 0xFFFF); + u16 vinc = (cheatsList[i].address >> 16) & 0xFFFF; + int inc = cheatsList[i].value; + + for(int x = 0; x < count; x++) + { + CPUWriteHalfWord(address, value); + address += inc; + value += vinc; + } + } + } + break; + case CBA_IF_FALSE: + if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value) + { + i++; + } + break; + case CBA_AND: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) & + cheatsList[i].value); + break; + case GSA_8_BIT_GS_WRITE2: + i++; + if(i < cheatsNumber) + { + if(extended & 4) + { + CPUWriteByte(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_16_BIT_GS_WRITE2: + i++; + if(i < cheatsNumber) + { + if(extended & 4) + { + CPUWriteHalfWord(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_32_BIT_GS_WRITE2: + i++; + if(i < cheatsNumber) + { + if(extended & 4) + { + CPUWriteMemory(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_16_BIT_ROM_PATCH2: + i++; + if(i < cheatsNumber) + { + if((cheatsList[i-1].status & 1) == 0) + { + u32 addr = ((cheatsList[i-1].value & 0x00FFFFFF) << 1) + 0x8000000; + if(CPUReadHalfWord(addr) != (cheatsList[i].address & 0xFFFF)) + { + cheatsList[i-1].oldValue = CPUReadHalfWord(addr); + cheatsList[i-1].status |= 1; + CHEAT_PATCH_ROM_16BIT(addr,cheatsList[i].address & 0xFFFF); + } + } + } + break; + case GSA_8_BIT_SLIDE: + i++; + if(i < cheatsNumber) + { + u32 addr = cheatsList[i-1].value; + u8 value = cheatsList[i].address; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff); + while(count > 0) + { + CPUWriteByte(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_16_BIT_SLIDE: + i++; + if(i < cheatsNumber) + { + u32 addr = cheatsList[i-1].value; + u16 value = cheatsList[i].address; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff)*2; + while(count > 0) + { + CPUWriteHalfWord(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_32_BIT_SLIDE: + i++; + if(i < cheatsNumber) + { + u32 addr = cheatsList[i-1].value; + u32 value = cheatsList[i].address; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff)*4; + while(count > 0) + { + CPUWriteMemory(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_8_BIT_IF_TRUE: + if(CPUReadByte(cheatsList[i].address) != cheatsList[i].value) + { + i++; + } + break; + case GSA_32_BIT_IF_TRUE: + if(CPUReadMemory(cheatsList[i].address) != cheatsList[i].value) + { + i++; + } + break; + case GSA_8_BIT_IF_FALSE: + if(CPUReadByte(cheatsList[i].address) == cheatsList[i].value) + { + i++; + } + break; + case GSA_32_BIT_IF_FALSE: + if(CPUReadMemory(cheatsList[i].address) == cheatsList[i].value) + { + i++; + } + break; + case GSA_8_BIT_FILL: + { + u32 addr = cheatsList[i].address; + u8 v = cheatsList[i].value & 0xff; + u32 end = addr + (cheatsList[i].value >> 8); + do + { + CPUWriteByte(addr, v); + addr++; + } + while (addr <= end); + } + break; + case GSA_16_BIT_FILL: + { + u32 addr = cheatsList[i].address; + u16 v = cheatsList[i].value & 0xffff; + u32 end = addr + ((cheatsList[i].value >> 16) << 1); + do + { + CPUWriteHalfWord(addr, v); + addr+=2; + } + while (addr <= end); + } + break; + case GSA_8_BIT_IF_TRUE2: + if(CPUReadByte(cheatsList[i].address) != cheatsList[i].value) + { + i+=2; + } + break; + case GSA_16_BIT_IF_TRUE2: + if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) + { + i+=2; + } + break; + case GSA_32_BIT_IF_TRUE2: + if(CPUReadMemory(cheatsList[i].address) != cheatsList[i].value) + { + i+=2; + } + break; + case GSA_8_BIT_IF_FALSE2: + if(CPUReadByte(cheatsList[i].address) == cheatsList[i].value) + { + i+=2; + } + break; + case GSA_16_BIT_IF_FALSE2: + if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value) + { + i+=2; + } + break; + case GSA_32_BIT_IF_FALSE2: + if(CPUReadMemory(cheatsList[i].address) == cheatsList[i].value) + { + i+=2; + } + break; + case GSA_SLOWDOWN: + // check if button was pressed and released, if so toggle our state + if((cheatsList[i].status & 4) && !(extended & 4)) + cheatsList[i].status ^= 1; + if(extended & 4) + cheatsList[i].status |= 4; + else + cheatsList[i].status &= ~4; + + if(cheatsList[i].status & 1) + ticks += 2*256*((cheatsList[i].value >> 8) & 255); + break; + case CBA_ADD: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) + + (u16)cheatsList[i].value); + break; + case CBA_OR: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) | + cheatsList[i].value); + break; + case CBA_LT: + if(CPUReadHalfWord(cheatsList[i].address) >= cheatsList[i].value) + i++; + break; + case CBA_GT: + if(CPUReadHalfWord(cheatsList[i].address) <= cheatsList[i].value) + i++; + break; + case CBA_SUPER: + { + int count = 2*cheatsList[i].value; + u32 address = cheatsList[i].address; + for(int x = 0; x < count; x++) + { + u8 b; + int res = x % 6; + if(res < 4) + b = (cheatsList[i].address >> (24-8*res)) & 0xFF; + else + b = (cheatsList[i].value >> (8 - 8*(res-4))) & 0x0FF; + CPUWriteByte(address, b); + address++; + if(x && !res) + i++; + } + if(count % 6) + i++; + } + break; + } + } + return ticks; +} + +void cheatsAdd(const char *codeStr, + const char *desc, + u32 address, + u32 value, + int code, + int size) +{ + if(cheatsNumber < 100) + { + int x = cheatsNumber; + cheatsList[x].code = code; + cheatsList[x].size = size; + cheatsList[x].address = address; + cheatsList[x].value = value; + strcpy(cheatsList[x].codestring, codeStr); + strcpy(cheatsList[x].desc, desc); + cheatsList[x].enabled = true; + cheatsList[x].status = 0; + + // we only store the old value for this simple codes. ROM patching + // is taken care when it actually patches the ROM + switch(cheatsList[x].size) + { + case INT_8_BIT_WRITE: + cheatsList[x].oldValue = CPUReadByte(address); + break; + case INT_16_BIT_WRITE: + cheatsList[x].oldValue = CPUReadHalfWord(address); + break; + case INT_32_BIT_WRITE: + cheatsList[x].oldValue = CPUReadMemory(address); + break; + } + cheatsNumber++; + } +} + +void cheatsDelete(int number, bool restore) +{ + if(number < cheatsNumber && number >= 0) + { + int x = number; + + if(restore) + { + switch(cheatsList[x].size) + { + case INT_8_BIT_WRITE: + CPUWriteByte(cheatsList[x].address, (u8)cheatsList[x].oldValue); + break; + case INT_16_BIT_WRITE: + CPUWriteHalfWord(cheatsList[x].address, (u16)cheatsList[x].oldValue); + break; + case INT_32_BIT_WRITE: + CPUWriteMemory(cheatsList[x].address, cheatsList[x].oldValue); + break; + case GSA_16_BIT_ROM_PATCH: + if(cheatsList[x].status & 1) + { + cheatsList[x].status &= ~1; + CHEAT_PATCH_ROM_16BIT(cheatsList[x].address, + cheatsList[x].oldValue); + } + break; + case GSA_16_BIT_ROM_PATCH2: + if(cheatsList[x].status & 1) + { + cheatsList[x].status &= ~1; + CHEAT_PATCH_ROM_16BIT(((cheatsList[x].value & 0x00FFFFFF) << 1)+ + 0x8000000, + cheatsList[x].oldValue); + } + break; + } + } + if((x+1) < cheatsNumber) + { + memcpy(&cheatsList[x], &cheatsList[x+1], sizeof(CheatsData)* + (cheatsNumber-x-1)); + } + cheatsNumber--; + } +} + +void cheatsDeleteAll(bool restore) +{ + for(int i = cheatsNumber-1; i >= 0; i--) + { + cheatsDelete(i, restore); + } +} + +void cheatsEnable(int i) +{ + if(i >= 0 && i < cheatsNumber) + { + cheatsList[i].enabled = true; + } +} + +void cheatsDisable(int i) +{ + if(i >= 0 && i < cheatsNumber) + { + switch(cheatsList[i].size) + { + case GSA_16_BIT_ROM_PATCH: + if(cheatsList[i].status & 1) + { + cheatsList[i].status &= ~1; + CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, + cheatsList[i].oldValue); + } + break; + case GSA_16_BIT_ROM_PATCH2: + if(cheatsList[i].status & 1) + { + cheatsList[i].status &= ~1; + CHEAT_PATCH_ROM_16BIT(((cheatsList[i].value & 0x00FFFFFF) << 1)+ + 0x8000000, + cheatsList[i].oldValue); + } + break; + } + cheatsList[i].enabled = false; + } +} + +bool cheatsVerifyCheatCode(const char *code, const char *desc) +{ + int len = strlen(code); + if(len != 11 && len != 13 && len != 17) + { + systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s'"), code); + return false; + } + + if(code[8] != ':') + { + systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s'"), code); + return false; + } + + int i; + for(i = 0; i < 8; i++) + { + if(!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CHEAT_CODE, + N_("Invalid cheat code '%s'"), code); + return false; + } + } + for(i = 9; i < len; i++) + { + if(!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CHEAT_CODE, + N_("Invalid cheat code '%s'"), code); + return false; + } + } + + u32 address = 0; + u32 value = 0; + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + sscanf(buffer, "%x", &address); + + switch(address >> 24) + { + case 2: + case 3: + break; + default: + systemMessage(MSG_INVALID_CHEAT_CODE_ADDRESS, + N_("Invalid cheat code address: %08x"), + address); + return false; + } + + strncpy(buffer, &code[9], 8); + sscanf(buffer, "%x", &value); + int type = 0; + if(len == 13) + type = 1; + if(len == 17) + type = 2; + cheatsAdd(code, desc, address, value, type, type); + return true; +} + +void cheatsAddCheatCode(const char *code, const char *desc) +{ + cheatsVerifyCheatCode(code, desc); +} + +void cheatsDecryptGSACode(u32& address, u32& value, bool v3) +{ + u32 rollingseed = 0xC6EF3720; + u32 seeds_v1[] = { 0x09F4FBBD, 0x9681884A, 0x352027E9, 0xF3DEE5A7 }; + u32 seeds_v3[] = { 0x7AA9648F, 0x7FAE6994, 0xC0EFAAD5, 0x42712C57 }; + u32 *seeds = v3 ? seeds_v3 : seeds_v1; + + int bitsleft = 32; + while (bitsleft > 0) + { + value -= ((((address << 4) + seeds[2]) ^ (address + rollingseed)) ^ + ((address >> 5) + seeds[3])); + address -= ((((value << 4) + seeds[0]) ^ (value + rollingseed)) ^ + ((value >> 5) + seeds[1])); + rollingseed -= 0x9E3779B9; + bitsleft--; + } +} + +void cheatsAddGSACode(const char *code, const char *desc, bool v3) +{ + if(strlen(code) != 16) + { + // wrong cheat + systemMessage(MSG_INVALID_GSA_CODE, + N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY")); + return; + } + + int i; + for(i = 0; i < 16; i++) + { + if(!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_GSA_CODE, + N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY")); + return; + } + } + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + strncpy(buffer, &code[8], 8); + buffer[8] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + cheatsDecryptGSACode(address, value, v3); + + if(value == 0x1DC0DE) + { + u32 gamecode = READ32LE(((u32 *)&rom[0xac])); + if(gamecode != address) + { + char buffer[5]; + *((u32 *)buffer) = address; + buffer[4] = 0; + char buffer2[5]; + *((u32 *)buffer2) = READ32LE(((u32 *)&rom[0xac])); + buffer2[4] = 0; + systemMessage(MSG_GBA_CODE_WARNING, N_("Warning: cheats are for game %s. Current game is %s.\nCodes may not work correctly."), + buffer, buffer2); + } + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, v3 ? 257 : 256, + UNKNOWN_CODE); + return; + } + if(isMultilineWithData(cheatsNumber-1)) + { + cheatsAdd(code, desc, address, value, v3 ? 257 : 256, UNKNOWN_CODE); + return; + } + if(v3) + { + int type = (address >> 25) & 127; + u32 addr = (address & 0x00F00000) << 4 | (address & 0x0003FFFF); + switch(type) + { + case 0x00: + if(address == 0) + { + type = (value >> 25) & 127; + addr = (value & 0x00F00000) << 4 | (value & 0x0003FFFF); + switch(type) + { + case 0x04: + cheatsAdd(code, desc, 0, value & 0x00FFFFFF, 257, GSA_SLOWDOWN); + break; + case 0x08: + cheatsAdd(code, desc, 0, addr, 257, GSA_8_BIT_GS_WRITE2); + break; + case 0x09: + cheatsAdd(code, desc, 0, addr, 257, GSA_16_BIT_GS_WRITE2); + break; + case 0x0a: + cheatsAdd(code, desc, 0, addr, 257, GSA_32_BIT_GS_WRITE2); + break; + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + cheatsAdd(code, desc, 0, value & 0x00FFFFFF, 257, GSA_16_BIT_ROM_PATCH2); + break; + case 0x40: + cheatsAdd(code, desc, 0, addr, 257, GSA_8_BIT_SLIDE); + break; + case 0x41: + cheatsAdd(code, desc, 0, addr, 257, GSA_16_BIT_SLIDE); + break; + case 0x42: + cheatsAdd(code, desc, 0, addr, 257, GSA_32_BIT_SLIDE); + break; + default: + cheatsAdd(code, desc, address, value, 257, UNKNOWN_CODE); + break; + } + } + else + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_FILL); + break; + case 0x01: + cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_FILL); + break; + case 0x02: + cheatsAdd(code, desc, addr, value, 257, INT_32_BIT_WRITE); + break; + case 0x04: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_TRUE); + break; + case 0x05: + cheatsAdd(code, desc, addr, value, 257, CBA_IF_TRUE); + break; + case 0x06: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_TRUE); + break; + case 0x08: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_FALSE); + break; + case 0x09: + cheatsAdd(code, desc, addr, value, 257, CBA_IF_FALSE); + break; + case 0x0a: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_FALSE); + break; + case 0x24: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_TRUE2); + break; + case 0x25: + cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_IF_TRUE2); + break; + case 0x26: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_TRUE2); + break; + case 0x28: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_FALSE2); + break; + case 0x29: + cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_IF_FALSE2); + break; + case 0x2a: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_FALSE2); + break; + default: + cheatsAdd(code, desc, address, value, 257, UNKNOWN_CODE); + break; + } + } + else + { + int type = (address >> 28) & 15; + switch(type) + { + case 0: + case 1: + case 2: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256, type); + break; + case 6: + address <<= 1; + type = (address >> 28) & 15; + if(type == 0x0c) + { + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256, + GSA_16_BIT_ROM_PATCH); + break; + } + // unsupported code + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + case 8: + switch((address >> 20) & 15) + { + case 1: + cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256, + GSA_8_BIT_GS_WRITE); + break; + case 2: + cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256, + GSA_16_BIT_GS_WRITE); + break; + case 3: + cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256, + GSA_32_BIT_GS_WRITE); + case 15: + cheatsAdd(code, desc, 0, value & 0xFF00, 256, GSA_SLOWDOWN); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + } + break; + case 0x0d: + if(address != 0xDEADFACE) + { + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256, + CBA_IF_TRUE); + } + else + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + } + } +} + +bool cheatsImportGSACodeFile(const char *name, int game, bool v3) +{ + FILE* f = gen_fopen(name, "rb"); + if(!f) + return false; + + int games = 0; + int len = 0; + gen_fseek(f, 0x1e, SEEK_CUR); + gen_fread(&games, 1, 4, f); + bool found = false; + int g = 0; + while(games > 0) + { + if(g == game) + { + found = true; + break; + } + gen_fread(&len, 1, 4, f); + gen_fseek(f,len,SEEK_CUR); + int codes = 0; + gen_fread(&codes, 1, 4, f); + while(codes > 0) + { + gen_fread(&len, 1, 4, f); + gen_fseek(f, len, SEEK_CUR); + gen_fseek(f, 8, SEEK_CUR); + gen_fread(&len, 1, 4, f); + gen_fseek(f, len*12, SEEK_CUR); + codes--; + } + games--; + g++; + } + if(found) + { + char desc[256]; + char code[17]; + gen_fread(&len, 1, 4, f); + gen_fseek(f, len, SEEK_CUR); + int codes = 0; + gen_fread(&codes, 1, 4, f); + while(codes > 0) + { + gen_fread(&len, 1, 4, f); + gen_fread(desc, 1, len, f); + desc[len] =0; + desc[31] = 0; + gen_fread(&len, 1, 4, f); + gen_fseek(f, len, SEEK_CUR); + gen_fseek(f, 4, SEEK_CUR); + gen_fread(&len, 1, 4, f); + while(len) + { + gen_fseek(f, 4, SEEK_CUR); + gen_fread(code, 1, 8, f); + gen_fseek(f, 4, SEEK_CUR); + gen_fread(&code[8], 1, 8, f); + code[16] = 0; + cheatsAddGSACode(code, desc, v3); + len -= 2; + } + codes--; + } + } + gen_fclose(f); + return false; +} + +void cheatsCBAReverseArray(u8 *array, u8 *dest) +{ + dest[0] = array[3]; + dest[1] = array[2]; + dest[2] = array[1]; + dest[3] = array[0]; + dest[4] = array[5]; + dest[5] = array[4]; +} + +void chatsCBAScramble(u8 *array, int count, u8 b) +{ + u8 *x = array + (count >> 3); + u8 *y = array + (b >> 3); + u32 z = *x & (1 << (count & 7)); + u32 x0 = (*x & (~(1 << (count & 7)))); + if (z != 0) + z = 1; + if ((*y & (1 << (b & 7))) != 0) + x0 |= (1 << (count & 7)); + *x = x0; + u32 temp = *y & (~(1 << (b & 7))); + if (z != 0) + temp |= (1 << (b & 7)); + *y = temp; +} + +u32 cheatsCBAGetValue(u8 *array) +{ + return array[0] | array[1]<<8 | array[2] << 16 | array[3]<<24; +} + +u16 cheatsCBAGetData(u8 *array) +{ + return array[4] | array[5]<<8; +} + +void cheatsCBAArrayToValue(u8 *array, u8 *dest) +{ + dest[0] = array[3]; + dest[1] = array[2]; + dest[2] = array[1]; + dest[3] = array[0]; + dest[4] = array[5]; + dest[5] = array[4]; +} + +void cheatsCBAParseSeedCode(u32 address, u32 value, u32 *array) +{ + array[0] = 1; + array[1] = value & 0xFF; + array[2] = (address >> 0x10) & 0xFF; + array[3] = (value >> 8) & 0xFF; + array[4] = (address >> 0x18) & 0x0F; + array[5] = address & 0xFFFF; + array[6] = address; + array[7] = value; +} + +u32 cheatsCBAEncWorker() +{ + u32 x = (cheatsCBATemporaryValue * 0x41c64e6d) + 0x3039; + u32 y = (x * 0x41c64e6d) + 0x3039; + u32 z = x >> 0x10; + x = ((y >> 0x10) & 0x7fff) << 0x0f; + z = (z << 0x1e) | x; + x = (y * 0x41c64e6d) + 0x3039; + cheatsCBATemporaryValue = x; + return z | ((x >> 0x10) & 0x7fff); +} + +#define ROR(v, s) \ + (((v) >> (s)) | (((v) & ((1 << (s))-1)) << (32 - (s)))) + +u32 cheatsCBACalcIndex(u32 x, u32 y) +{ + if(y != 0) + { + if(y == 1) + x = 0; + else if(x == y) + x = 0; + if(y < 1) + return x; + else if(x < y) + return x; + u32 x0 = 1; + + while(y < 0x10000000) + { + if(y < x) + { + y = y << 4; + x0 = x0 << 4; + } + else break; + } + + while(y < 0x80000000) + { + if(y < x) + { + y = y << 1; + x0 = x0 << 1; + } + else break; + } + +loop: + u32 z = 0; + if(x >= y) + x -= y; + if(x >= (y >> 1)) + { + x -= (y >> 1); + z |= ROR(x0, 1); + } + if(x >= (y >> 2)) + { + x -= (y >> 2); + z |= ROR(x0, 2); + } + if(x >= (y >> 3)) + { + x -= (y >> 3); + z |= ROR(x0, 3); + } + + u32 temp = x0; + + if(x != 0) + { + x0 = x0 >> 4; + if(x0 != 0) + { + y = y >> 4; + goto loop; + } + } + + z = z & 0xe0000000; + + if(z != 0) + { + if((temp & 7) == 0) + return x; + } + else + return x; + + if((z & ROR(temp, 3)) != 0) + x += y >> 3; + if((z & ROR(temp, 2)) != 0) + x += y >> 2; + if((z & ROR(temp, 1)) != 0) + x += y >> 1; + return x; + } + else + {} + // should not happen in the current code + return 0; +} + +void cheatsCBAUpdateSeedBuffer(u32 a, u8 *buffer, int count) +{ + int i; + for(i = 0; i < count; i++) + buffer[i] = i; + for(i = 0; (u32)i < a; i++) + { + u32 a = cheatsCBACalcIndex(cheatsCBAEncWorker(), count); + u32 b = cheatsCBACalcIndex(cheatsCBAEncWorker(), count); + u32 t = buffer[a]; + buffer[a] = buffer[b]; + buffer[b] = t; + } +} + +void cheatsCBAChangeEncryption(u32 *seed) +{ + int i; + + cheatsCBATemporaryValue = (seed[1] ^ 0x1111); + cheatsCBAUpdateSeedBuffer(0x50, cheatsCBASeedBuffer, 0x30); + cheatsCBATemporaryValue = 0x4efad1c3; + + for(i = 0; (u32)i < seed[4]; i++) + { + cheatsCBATemporaryValue = cheatsCBAEncWorker(); + } + cheatsCBASeed[2] = cheatsCBAEncWorker(); + cheatsCBASeed[3] = cheatsCBAEncWorker(); + + cheatsCBATemporaryValue = seed[3] ^ 0xf254; + + for(i = 0; (u32)i < seed[3]; i++) + { + cheatsCBATemporaryValue = cheatsCBAEncWorker(); + } + + cheatsCBASeed[0] = cheatsCBAEncWorker(); + cheatsCBASeed[1] = cheatsCBAEncWorker(); + + *((u32 *)&cheatsCBACurrentSeed[0]) = seed[6]; + *((u32 *)&cheatsCBACurrentSeed[4]) = seed[7]; + *((u32 *)&cheatsCBACurrentSeed[8]) = 0; +} + +u16 cheatsCBAGenValue(u32 x, u32 y, u32 z) +{ + y <<= 0x10; + z <<= 0x10; + x <<= 0x18; + u32 x0 = (int)y >> 0x10; + z = (int)z >> 0x10; + x = (int)x >> 0x10; + for(int i = 0; i < 8; i++) + { + u32 temp = z ^ x; + if ((int)temp >= 0) + { + temp = z << 0x11; + } + else + { + temp = z << 0x01; + temp ^= x0; + temp = temp << 0x10; + } + z = (int)temp >> 0x10; + temp = x << 0x11; + x = (int)temp >> 0x10; + } + return z & 0xffff; +} + +void cheatsCBAGenTable() +{ + for (int i = 0; i < 0x100; i++) + { + cheatsCBATable[i] = cheatsCBAGenValue(i, 0x1021, 0); + } + cheatsCBATableGenerated = true; +} + +u16 cheatsCBACalcCRC(u8 *rom, int count) +{ + u32 crc = 0xffffffff; + + if (count & 3) + { + // 0x08000EAE + } + else + { + count = (count >> 2) - 1; + if(count != -1) + { + while(count != -1) + { + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + count--; + } + } + } + return crc & 0xffff; +} + +void cheatsCBADecrypt(u8 *decrypt) +{ + u8 buffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + u8 *array = &buffer[1]; + + cheatsCBAReverseArray(decrypt, array); + + for(int count = 0x2f; count >= 0; count--) + { + chatsCBAScramble(array, count, cheatsCBASeedBuffer[count]); + } + cheatsCBAArrayToValue(array, decrypt); + *((u32 *)decrypt) = cheatsCBAGetValue(decrypt) ^ + cheatsCBASeed[0]; + *((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt) ^ + cheatsCBASeed[1]) & 0xffff; + + cheatsCBAReverseArray(decrypt, array); + + u32 cs = cheatsCBAGetValue(cheatsCBACurrentSeed); + for(int i = 0; i <= 4; i++) + { + array[i] = ((cs >> 8) ^ array[i+1]) ^ array[i] ; + } + + array[5] = (cs >> 8) ^ array[5]; + + for(int j = 5; j >=0; j--) + { + array[j] = (cs ^ array[j-1]) ^ array[j]; + } + + cheatsCBAArrayToValue(array, decrypt); + + *((u32 *)decrypt) = cheatsCBAGetValue(decrypt) + ^ cheatsCBASeed[2]; + *((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt) + ^ cheatsCBASeed[3]) & 0xffff; +} + +int cheatsCBAGetCount() +{ + int count = 0; + for(int i = 0; i < cheatsNumber; i++) + { + if(cheatsList[i].code == 512) + count++; + } + return count; +} + +bool cheatsCBAShouldDecrypt() +{ + for(int i = 0; i < cheatsNumber; i++) + { + if(cheatsList[i].code == 512) + { + return (cheatsList[i].codestring[0] == '9'); + } + } + return false; +} + +void cheatsAddCBACode(const char *code, const char *desc) +{ + if(strlen(code) != 13) + { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + + int i; + for(i = 0; i < 8; i++) + { + if(!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + } + + if(code[8] != ' ') + { + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + + for(i = 9; i < 13; i++) + { + if(!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + } + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + strncpy(buffer, &code[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u8 array[8] = { + address & 255, + (address >> 8) & 255, + (address >> 16) & 255, + (address >> 24) & 255, + (value & 255), + (value >> 8) & 255, + 0, + 0 + }; + + if(cheatsCBAGetCount() == 0 && + (address >> 28) == 9) + { + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, UNKNOWN_CODE); + } + else + { + if(cheatsCBAShouldDecrypt()) + cheatsCBADecrypt(array); + + address = READ32LE(((u32 *)array)); + value = READ16LE(((u16 *)&array[4])); + + int type = (address >> 28) & 15; + + if(isMultilineWithData(cheatsNumber-1)) + { + cheatsAdd(code, desc, address, value, 512, UNKNOWN_CODE); + return; + } + + switch(type) + { + case 0x00: + { + if(!cheatsCBATableGenerated) + cheatsCBAGenTable(); + u32 crc = cheatsCBACalcCRC(rom, 0x10000); + if(crc != address) + { + systemMessage(MSG_CBA_CODE_WARNING, + N_("Warning: Codes seem to be for a different game.\nCodes may not work correctly.")); + } + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + UNKNOWN_CODE); + } + break; + case 0x02: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_OR); + break; + case 0x03: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + INT_8_BIT_WRITE); + break; + case 0x04: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_SLIDE_CODE); + break; + case 0x05: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_SUPER); + break; + case 0x06: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_AND); + break; + case 0x07: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_IF_TRUE); + break; + case 0x08: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + INT_16_BIT_WRITE); + break; + case 0x0a: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_IF_FALSE); + break; + case 0x0b: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_LT); + break; + case 0x0c: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_GT); + break; + case 0x0d: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_IF_KEYS_PRESSED); + break; + case 0x0e: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_ADD); + break; + default: + // unsupported code + cheatsAdd(code, desc, address & 0xFFFFFFFF, value, 512, + UNKNOWN_CODE); + break; + } + } +} + +void cheatsSaveGame(gzFile file) +{ + utilWriteInt(file, cheatsNumber); + + utilGzWrite(file, cheatsList, sizeof(cheatsList)); +} + +void cheatsReadGame(gzFile file) +{ + cheatsNumber = 0; + + cheatsNumber = utilReadInt(file); + + utilGzRead(file, cheatsList, sizeof(cheatsList)); + + bool firstCodeBreaker = true; + + for(int i = 0; i < cheatsNumber; i++) + { + cheatsList[i].status = 0; + if(!cheatsList[i].codestring[0]) + { + switch(cheatsList[i].size) + { + case 0: + sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + cheatsList[i].value); + break; + case 1: + sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + cheatsList[i].value); + break; + case 2: + sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + cheatsList[i].value); + break; + } + } + + if(cheatsList[i].enabled) + { + cheatsEnable(i); + } + + if(cheatsList[i].code == 512 && firstCodeBreaker) + { + firstCodeBreaker = false; + char buffer[10]; + strncpy(buffer, cheatsList[i].codestring, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + if((address >> 28) == 9) + { + strncpy(buffer, &cheatsList[i].codestring[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + } + } + } +} + +void cheatsSaveCheatList(const char *file) +{ + if(cheatsNumber == 0) + return; + FILE* f = gen_fopen(file, "wb"); + if(f == NULL) + return; + int version = 1; + gen_fwrite(&version, 1, sizeof(version), f); + int type = 0; + gen_fwrite(&type, 1, sizeof(type), f); + gen_fwrite(&cheatsNumber, 1, sizeof(cheatsNumber), f); + gen_fwrite(cheatsList, 1, sizeof(cheatsList), f); + gen_fclose(f); +} + +bool cheatsLoadCheatList(const char *file) +{ + cheatsNumber = 0; + + int count = 0; + + FILE* f = gen_fopen(file, "rb"); + + if(f == NULL) + return false; + + int version = 0; + + if(gen_fread(&version, 1, sizeof(version), f) != sizeof(version)) + { + gen_fclose(f); + return false; + } + + if(version != 1) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, + N_("Unsupported cheat list version %d"), version); + gen_fclose(f); + return false; + } + + int type = 0; + if(gen_fread(&type, 1, sizeof(type), f) != sizeof(type)) + { + gen_fclose(f); + return false; + } + + if(type != 0) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, + N_("Unsupported cheat list type %d"), type); + gen_fclose(f); + return false; + } + + if(gen_fread(&count, 1, sizeof(count), f) != sizeof(count)) + { + gen_fclose(f); + return false; + } + + if(gen_fread(cheatsList, 1, sizeof(cheatsList), f) != sizeof(cheatsList)) + { + gen_fclose(f); + return false; + } + + bool firstCodeBreaker = true; + + for(int i = 0; i < count; i++) + { + cheatsList[i].status = 0; // remove old status as it is not used + if(!cheatsList[i].codestring[0]) + { + switch(cheatsList[i].size) + { + case 0: + sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + cheatsList[i].value); + break; + case 1: + sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + cheatsList[i].value); + break; + case 2: + sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + cheatsList[i].value); + break; + } + } + + if(cheatsList[i].code == 512 && firstCodeBreaker) + { + firstCodeBreaker = false; + char buffer[10]; + strncpy(buffer, cheatsList[i].codestring, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + if((address >> 28) == 9) + { + strncpy(buffer, &cheatsList[i].codestring[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + } + } + } + cheatsNumber = count; + gen_fclose(f); + return true; +} + +extern int *extCpuLoopTicks; +extern int *extClockTicks; +extern int *extTicks; +extern int cpuSavedTicks; + +extern void debuggerBreakOnWrite(u32 *, u32, u32, int); + +#define CPU_BREAK_LOOP \ + cpuSavedTicks = cpuSavedTicks - *extCpuLoopTicks;\ + *extCpuLoopTicks = *extClockTicks;\ + *extTicks = *extClockTicks; + +void cheatsWriteMemory(u32 *address, u32 value, u32 mask) +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if(cheatsNumber == 0) + { + debuggerBreakOnWrite(address, *address, value, 2); + CPU_BREAK_LOOP; + *address = value; + return; + } +#endif +#endif +} + +void cheatsWriteHalfWord(u16 *address, u16 value, u16 mask) +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if(cheatsNumber == 0) + { + debuggerBreakOnWrite((u32 *)address, *address, value, 1); + CPU_BREAK_LOOP; + *address = value; + return; + } +#endif +#endif +} + +#if defined BKPT_SUPPORT && defined SDL +void cheatsWriteByte(u8 *address, u8 value) +#else +void cheatsWriteByte(u8 *, u8) +#endif +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if(cheatsNumber == 0) + { + debuggerBreakOnWrite((u32 *)address, *address, value, 0); + CPU_BREAK_LOOP; + *address = value; + return; + } +#endif +#endif +} diff --git a/src/Cheats.h b/src/Cheats.h new file mode 100644 index 0000000..61d8e90 --- /dev/null +++ b/src/Cheats.h @@ -0,0 +1,55 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef GBA_CHEATS_H +#define GBA_CHEATS_H + +struct CheatsData + { + int code; + int size; + int status; + bool enabled; + u32 address; + u32 value; + u32 oldValue; + char codestring[20]; + char desc[32]; + }; + +extern void cheatsAdd(const char *,const char *,u32,u32,int,int); +extern void cheatsAddCheatCode(const char *code, const char *desc); +extern void cheatsAddGSACode(const char *code, const char *desc, bool v3); +extern void cheatsAddCBACode(const char *code, const char *desc); +extern bool cheatsImportGSACodeFile(const char *name, int game, bool v3); +extern void cheatsDelete(int number, bool restore); +extern void cheatsDeleteAll(bool restore); +extern void cheatsEnable(int number); +extern void cheatsDisable(int number); +extern void cheatsSaveGame(gzFile file); +extern void cheatsReadGame(gzFile file); +extern void cheatsSaveCheatList(const char *file); +extern bool cheatsLoadCheatList(const char *file); +extern void cheatsWriteMemory(u32 *, u32, u32); +extern void cheatsWriteHalfWord(u16 *, u16, u16); +extern void cheatsWriteByte(u8 *, u8); +extern int cheatsCheckKeys(u32,u32); +extern int cheatsNumber; +extern CheatsData cheatsList[100]; +#endif // GBA_CHEATS_H diff --git a/src/EEprom.cpp b/src/EEprom.cpp new file mode 100644 index 0000000..86ec201 --- /dev/null +++ b/src/EEprom.cpp @@ -0,0 +1,214 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "EEprom.h" +#include "Util.h" + +extern int cpuDmaCount; + +int eepromMode = EEPROM_IDLE; +int eepromByte = 0; +int eepromBits = 0; +int eepromAddress = 0; +u8 eepromData[0x2000]; +u8 eepromBuffer[16]; +bool eepromInUse = false; +int eepromSize = 512; + +variable_desc eepromSaveData[] = { + { &eepromMode, sizeof(int) }, + { &eepromByte, sizeof(int) }, + { &eepromBits , sizeof(int) }, + { &eepromAddress , sizeof(int) }, + { &eepromInUse, sizeof(bool) }, + { &eepromData[0], 512 }, + { &eepromBuffer[0], 16 }, + { NULL, 0 } + }; + +void eepromReset() +{ + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + eepromAddress = 0; + eepromInUse = false; + eepromSize = 512; +} + +void eepromSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, eepromSaveData); + utilWriteInt(gzFile, eepromSize); + utilGzWrite(gzFile, eepromData, 0x2000); +} + +void eepromReadGame(gzFile gzFile, int version) +{ + utilReadData(gzFile, eepromSaveData); + if(version >= SAVE_GAME_VERSION_3) + { + eepromSize = utilReadInt(gzFile); + utilGzRead(gzFile, eepromData, 0x2000); + } + else + { + // prior to 0.7.1, only 4K EEPROM was supported + eepromSize = 512; + } +} + + +int eepromRead(u32 /* address */) +{ + switch(eepromMode) + { + case EEPROM_IDLE: + case EEPROM_READADDRESS: + case EEPROM_WRITEDATA: + return 1; + case EEPROM_READDATA: + { + eepromBits++; + if(eepromBits == 4) + { + eepromMode = EEPROM_READDATA2; + eepromBits = 0; + eepromByte = 0; + } + return 0; + } + case EEPROM_READDATA2: + { + int data = 0; + int address = eepromAddress << 3; + int mask = 1 << (7 - (eepromBits & 7)); + data = (eepromData[address+eepromByte] & mask) ? 1 : 0; + eepromBits++; + if((eepromBits & 7) == 0) + eepromByte++; + if(eepromBits == 0x40) + eepromMode = EEPROM_IDLE; + return data; + } + default: + return 0; + } + return 1; +} + +void eepromWrite(u32 /* address */, u8 value) +{ + if(cpuDmaCount == 0) + return; + int bit = value & 1; + switch(eepromMode) + { + case EEPROM_IDLE: + eepromByte = 0; + eepromBits = 1; + eepromBuffer[eepromByte] = bit; + eepromMode = EEPROM_READADDRESS; + break; + case EEPROM_READADDRESS: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if((eepromBits & 7) == 0) + { + eepromByte++; + } + if(cpuDmaCount == 0x11 || cpuDmaCount == 0x51) + { + if(eepromBits == 0x11) + { + eepromInUse = true; + eepromSize = 0x2000; + eepromAddress = ((eepromBuffer[0] & 0x3F) << 8) | + ((eepromBuffer[1] & 0xFF)); + if(!(eepromBuffer[0] & 0x40)) + { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } + else + { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } + else + { + if(eepromBits == 9) + { + eepromInUse = true; + eepromAddress = (eepromBuffer[0] & 0x3F); + if(!(eepromBuffer[0] & 0x40)) + { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } + else + { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } + break; + case EEPROM_READDATA: + case EEPROM_READDATA2: + // should we reset here? + eepromMode = EEPROM_IDLE; + break; + case EEPROM_WRITEDATA: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if((eepromBits & 7) == 0) + { + eepromByte++; + } + if(eepromBits == 0x40) + { + eepromInUse = true; + // write data; + for(int i = 0; i < 8; i++) + { + eepromData[(eepromAddress << 3) + i] = eepromBuffer[i]; + } + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + else if(eepromBits == 0x41) + { + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + } + break; + } +} + diff --git a/src/EEprom.h b/src/EEprom.h new file mode 100644 index 0000000..4bf23af --- /dev/null +++ b/src/EEprom.h @@ -0,0 +1,38 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_EEPROM_H +#define VBA_EEPROM_H + +extern void eepromSaveGame(gzFile gzFile); +extern void eepromReadGame(gzFile gzFile, int version); +extern int eepromRead(u32 address); +extern void eepromWrite(u32 address, u8 value); +extern void eepromReset(); +extern u8 eepromData[0x2000]; +extern bool eepromInUse; +extern int eepromSize; + +#define EEPROM_IDLE 0 +#define EEPROM_READADDRESS 1 +#define EEPROM_READDATA 2 +#define EEPROM_READDATA2 3 +#define EEPROM_WRITEDATA 4 + +#endif // VBA_EEPROM_H diff --git a/src/Flash.cpp b/src/Flash.cpp new file mode 100644 index 0000000..41c189a --- /dev/null +++ b/src/Flash.cpp @@ -0,0 +1,288 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include "GBA.h" +#include "Globals.h" +#include "Flash.h" +#include "Sram.h" +#include "Util.h" + +#define FLASH_READ_ARRAY 0 +#define FLASH_CMD_1 1 +#define FLASH_CMD_2 2 +#define FLASH_AUTOSELECT 3 +#define FLASH_CMD_3 4 +#define FLASH_CMD_4 5 +#define FLASH_CMD_5 6 +#define FLASH_ERASE_COMPLETE 7 +#define FLASH_PROGRAM 8 +#define FLASH_SETBANK 9 + +u8 flashSaveMemory[0x20000]; +int flashState = FLASH_READ_ARRAY; +int flashReadState = FLASH_READ_ARRAY; +int flashSize = 0x10000; +int flashDeviceID = 0x1b; +int flashManufacturerID = 0x32; +int flashBank = 0; + +static variable_desc flashSaveData[] = { + { &flashState, sizeof(int) }, + { &flashReadState, sizeof(int) }, + { &flashSaveMemory[0], 0x10000 }, + { NULL, 0 } + }; + +static variable_desc flashSaveData2[] = { + { &flashState, sizeof(int) }, + { &flashReadState, sizeof(int) }, + { &flashSize, sizeof(int) }, + { &flashSaveMemory[0], 0x20000 }, + { NULL, 0 } + }; + +static variable_desc flashSaveData3[] = { + { &flashState, sizeof(int) }, + { &flashReadState, sizeof(int) }, + { &flashSize, sizeof(int) }, + { &flashBank, sizeof(int) }, + { &flashSaveMemory[0], 0x20000 }, + { NULL, 0 } + }; + +void flashReset() +{ + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + flashBank = 0; +} + +void flashSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, flashSaveData3); +} + +void flashReadGame(gzFile gzFile, int version) +{ + if(version < SAVE_GAME_VERSION_5) + utilReadData(gzFile, flashSaveData); + else if(version < SAVE_GAME_VERSION_7) + { + utilReadData(gzFile, flashSaveData2); + flashBank = 0; + flashSetSize(flashSize); + } + else + { + utilReadData(gzFile, flashSaveData3); + } +} + +void flashSetSize(int size) +{ + // log("Setting flash size to %d\n", size); + flashSize = size; + if(size == 0x10000) + { + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + } + else + { + flashDeviceID = 0x13; //0x09; + flashManufacturerID = 0x62; //0xc2; + } +} + +u8 flashRead(u32 address) +{ + // log("Reading %08x from %08x\n", address, reg[15].I); + // log("Current read state is %d\n", flashReadState); + address &= 0xFFFF; + + switch(flashReadState) + { + case FLASH_READ_ARRAY: + return flashSaveMemory[(flashBank << 16) + address]; + case FLASH_AUTOSELECT: + switch(address & 0xFF) + { + case 0: + // manufacturer ID + return flashManufacturerID; + case 1: + // device ID + return flashDeviceID; + } + break; + case FLASH_ERASE_COMPLETE: + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + return 0xFF; + }; + return 0; +} + +void flashSaveDecide(u32 address, u8 byte) +{ + // log("Deciding save type %08x\n", address); + if(address == 0x0e005555) + { + saveType = 2; + cpuSaveGameFunc = flashWrite; + } + else + { + saveType = 1; + cpuSaveGameFunc = sramWrite; + } + + (*cpuSaveGameFunc)(address, byte); +} + +void flashWrite(u32 address, u8 byte) +{ + // log("Writing %02x at %08x\n", byte, address); + // log("Current state is %d\n", flashState); + address &= 0xFFFF; + switch(flashState) + { + case FLASH_READ_ARRAY: + if(address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + break; + case FLASH_CMD_1: + if(address == 0x2AAA && byte == 0x55) + flashState = FLASH_CMD_2; + else + flashState = FLASH_READ_ARRAY; + break; + case FLASH_CMD_2: + if(address == 0x5555) + { + if(byte == 0x90) + { + flashState = FLASH_AUTOSELECT; + flashReadState = FLASH_AUTOSELECT; + } + else if(byte == 0x80) + { + flashState = FLASH_CMD_3; + } + else if(byte == 0xF0) + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + else if(byte == 0xA0) + { + flashState = FLASH_PROGRAM; + } + else if(byte == 0xB0 && flashSize == 0x20000) + { + flashState = FLASH_SETBANK; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_3: + if(address == 0x5555 && byte == 0xAA) + { + flashState = FLASH_CMD_4; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_4: + if(address == 0x2AAA && byte == 0x55) + { + flashState = FLASH_CMD_5; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_5: + if(byte == 0x30) + { + // SECTOR ERASE + memset(&flashSaveMemory[(flashBank << 16) + (address & 0xF000)], + 0, + 0x1000); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashReadState = FLASH_ERASE_COMPLETE; + } + else if(byte == 0x10) + { + // CHIP ERASE + memset(flashSaveMemory, 0, flashSize); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashReadState = FLASH_ERASE_COMPLETE; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_AUTOSELECT: + if(byte == 0xF0) + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + else if(address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_PROGRAM: + flashSaveMemory[(flashBank<<16)+address] = byte; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + case FLASH_SETBANK: + if(address == 0) + { + flashBank = (byte & 1); + } + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + } +} diff --git a/src/Flash.h b/src/Flash.h new file mode 100644 index 0000000..b00a35e --- /dev/null +++ b/src/Flash.h @@ -0,0 +1,33 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_FLASH_H +#define VBA_FLASH_H + +extern void flashSaveGame(gzFile gzFile); +extern void flashReadGame(gzFile gzFile, int version); +extern u8 flashRead(u32 address); +extern void flashWrite(u32 address, u8 byte); +extern u8 flashSaveMemory[0x20000]; +extern void flashSaveDecide(u32 address, u8 byte); +extern void flashReset(); +extern void flashSetSize(int size); + +extern int flashSize; +#endif // VBA_FLASH_H diff --git a/src/GBA.cpp b/src/GBA.cpp new file mode 100644 index 0000000..8547664 --- /dev/null +++ b/src/GBA.cpp @@ -0,0 +1,4360 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "sdfileio.h" +#include +#include +#include +#include +#include + +#include "GBA.h" +#include "GBAinline.h" +#include "Globals.h" +#include "Gfx.h" +#include "EEprom.h" +#include "Flash.h" +#include "Sound.h" +#include "Sram.h" +#include "bios.h" +#include "unzip.h" +#include "Cheats.h" +#include "NLS.h" +#include "elf.h" +#include "Util.h" +#include "Port.h" +#include "agbprint.h" +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +#define CPU_BREAK_LOOP \ + cpuSavedTicks = cpuSavedTicks - *extCpuLoopTicks;\ + *extCpuLoopTicks = *extClockTicks; + +#define CPU_BREAK_LOOP_2 \ + cpuSavedTicks = cpuSavedTicks - *extCpuLoopTicks;\ + *extCpuLoopTicks = *extClockTicks;\ + *extTicks = *extClockTicks; + +extern int emulating; + +int cpuDmaTicksToUpdate = 0; +int cpuDmaCount = 0; +bool cpuDmaHack = 0; +u32 cpuDmaLast = 0; +int dummyAddress = 0; + +int *extCpuLoopTicks = NULL; +int *extClockTicks = NULL; +int *extTicks = NULL; + +int gbaSaveType = 0; // used to remember the save type on reset +bool intState = false; +bool stopState = false; +bool holdState = false; +int holdType = 0; +bool cpuSramEnabled = true; +bool cpuFlashEnabled = true; +bool cpuEEPROMEnabled = true; +bool cpuEEPROMSensorEnabled = false; + +#ifdef PROFILING +int profilingTicks = 0; +int profilingTicksReload = 0; +static char *profilBuffer = NULL; +static int profilSize = 0; +static u32 profilLowPC = 0; +static int profilScale = 0; +#endif +bool freezeWorkRAM[0x40000]; +bool freezeInternalRAM[0x8000]; +int lcdTicks = 960; +bool timer0On = false; +int timer0Ticks = 0; +int timer0Reload = 0; +int timer0ClockReload = 0; +bool timer1On = false; +int timer1Ticks = 0; +int timer1Reload = 0; +int timer1ClockReload = 0; +bool timer2On = false; +int timer2Ticks = 0; +int timer2Reload = 0; +int timer2ClockReload = 0; +bool timer3On = false; +int timer3Ticks = 0; +int timer3Reload = 0; +int timer3ClockReload = 0; +u32 dma0Source = 0; +u32 dma0Dest = 0; +u32 dma1Source = 0; +u32 dma1Dest = 0; +u32 dma2Source = 0; +u32 dma2Dest = 0; +u32 dma3Source = 0; +u32 dma3Dest = 0; +void (*cpuSaveGameFunc)(u32,u8) = flashSaveDecide; +void (*renderLine)() = mode0RenderLine; +bool fxOn = false; +bool windowOn = false; +int frameCount = 0; +char buffer[1024]; +FILE* out = NULL; +u32 lastTime = 0; +int count = 0; + +int capture = 0; +int capturePrevious = 0; +int captureNumber = 0; + +const int TIMER_TICKS[4] = + { + 1, + 64, + 256, + 1024 + }; + +const int thumbCycles[] = + { + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 4 + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 5 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 6 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 7 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 8 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 9 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // a + 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, // b + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, // d + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // e + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 // f + }; + +const int gamepakRamWaitState[4] = + { + 4, 3, 2, 8 + }; +const int gamepakWaitState[8] = + { + 4, 3, 2, 8, 4, 3, 2, 8 + }; +const int gamepakWaitState0[8] = + { + 2, 2, 2, 2, 1, 1, 1, 1 + }; +const int gamepakWaitState1[8] = + { + 4, 4, 4, 4, 1, 1, 1, 1 + }; +const int gamepakWaitState2[8] = + { + 8, 8, 8, 8, 1, 1, 1, 1 + }; + +int memoryWait[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +int memoryWait32[16] = + { 0, 0, 9, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 0 }; +int memoryWaitSeq[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; +int memoryWaitSeq32[16] = + { 2, 0, 3, 0, 0, 2, 2, 0, 4, 4, 8, 8, 16, 16, 8, 0 }; +int memoryWaitFetch[16] = + { 3, 0, 3, 0, 0, 1, 1, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +int memoryWaitFetch32[16] = + { 6, 0, 6, 0, 0, 2, 2, 0, 8, 8, 8, 8, 8, 8, 8, 0 }; + +const int cpuMemoryWait[16] = + { + 0, 0, 2, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 0, 0 + }; +const int cpuMemoryWait32[16] = + { + 0, 0, 3, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 0, 0 + }; + +const bool memory32[16] = + { + true, false, false, true, true, false, false, true, false, false, false, false, false, false, true, false + }; + +u8 biosProtected[4]; + +#ifdef WORDS_BIGENDIAN +bool cpuBiosSwapped = false; +#endif + +u32 myROM[] = { + 0xEA000006, + 0xEA000093, + 0xEA000006, + 0x00000000, + 0x00000000, + 0x00000000, + 0xEA000088, + 0x00000000, + 0xE3A00302, + 0xE1A0F000, + 0xE92D5800, + 0xE55EC002, + 0xE28FB03C, + 0xE79BC10C, + 0xE14FB000, + 0xE92D0800, + 0xE20BB080, + 0xE38BB01F, + 0xE129F00B, + 0xE92D4004, + 0xE1A0E00F, + 0xE12FFF1C, + 0xE8BD4004, + 0xE3A0C0D3, + 0xE129F00C, + 0xE8BD0800, + 0xE169F00B, + 0xE8BD5800, + 0xE1B0F00E, + 0x0000009C, + 0x0000009C, + 0x0000009C, + 0x0000009C, + 0x000001F8, + 0x000001F0, + 0x000000AC, + 0x000000A0, + 0x000000FC, + 0x00000168, + 0xE12FFF1E, + 0xE1A03000, + 0xE1A00001, + 0xE1A01003, + 0xE2113102, + 0x42611000, + 0xE033C040, + 0x22600000, + 0xE1B02001, + 0xE15200A0, + 0x91A02082, + 0x3AFFFFFC, + 0xE1500002, + 0xE0A33003, + 0x20400002, + 0xE1320001, + 0x11A020A2, + 0x1AFFFFF9, + 0xE1A01000, + 0xE1A00003, + 0xE1B0C08C, + 0x22600000, + 0x42611000, + 0xE12FFF1E, + 0xE92D0010, + 0xE1A0C000, + 0xE3A01001, + 0xE1500001, + 0x81A000A0, + 0x81A01081, + 0x8AFFFFFB, + 0xE1A0000C, + 0xE1A04001, + 0xE3A03000, + 0xE1A02001, + 0xE15200A0, + 0x91A02082, + 0x3AFFFFFC, + 0xE1500002, + 0xE0A33003, + 0x20400002, + 0xE1320001, + 0x11A020A2, + 0x1AFFFFF9, + 0xE0811003, + 0xE1B010A1, + 0xE1510004, + 0x3AFFFFEE, + 0xE1A00004, + 0xE8BD0010, + 0xE12FFF1E, + 0xE0010090, + 0xE1A01741, + 0xE2611000, + 0xE3A030A9, + 0xE0030391, + 0xE1A03743, + 0xE2833E39, + 0xE0030391, + 0xE1A03743, + 0xE2833C09, + 0xE283301C, + 0xE0030391, + 0xE1A03743, + 0xE2833C0F, + 0xE28330B6, + 0xE0030391, + 0xE1A03743, + 0xE2833C16, + 0xE28330AA, + 0xE0030391, + 0xE1A03743, + 0xE2833A02, + 0xE2833081, + 0xE0030391, + 0xE1A03743, + 0xE2833C36, + 0xE2833051, + 0xE0030391, + 0xE1A03743, + 0xE2833CA2, + 0xE28330F9, + 0xE0000093, + 0xE1A00840, + 0xE12FFF1E, + 0xE3A00001, + 0xE3A01001, + 0xE92D4010, + 0xE3A0C301, + 0xE3A03000, + 0xE3A04001, + 0xE3500000, + 0x1B000004, + 0xE5CC3301, + 0xEB000002, + 0x0AFFFFFC, + 0xE8BD4010, + 0xE12FFF1E, + 0xE5CC3208, + 0xE15C20B8, + 0xE0110002, + 0x10200002, + 0x114C00B8, + 0xE5CC4208, + 0xE12FFF1E, + 0xE92D500F, + 0xE3A00301, + 0xE1A0E00F, + 0xE510F004, + 0xE8BD500F, + 0xE25EF004, + 0xE59FD044, + 0xE92D5000, + 0xE14FC000, + 0xE10FE000, + 0xE92D5000, + 0xE3A0C302, + 0xE5DCE09C, + 0xE35E00A5, + 0x1A000004, + 0x05DCE0B4, + 0x021EE080, + 0xE28FE004, + 0x159FF018, + 0x059FF018, + 0xE59FD018, + 0xE8BD5000, + 0xE169F00C, + 0xE8BD5000, + 0xE25EF004, + 0x03007FF0, + 0x09FE2000, + 0x09FFC000, + 0x03007FE0 + }; + +variable_desc saveGameStruct[] = { + { &DISPCNT , sizeof(u16) }, + { &DISPSTAT , sizeof(u16) }, + { &VCOUNT , sizeof(u16) }, + { &BG0CNT , sizeof(u16) }, + { &BG1CNT , sizeof(u16) }, + { &BG2CNT , sizeof(u16) }, + { &BG3CNT , sizeof(u16) }, + { &BG0HOFS , sizeof(u16) }, + { &BG0VOFS , sizeof(u16) }, + { &BG1HOFS , sizeof(u16) }, + { &BG1VOFS , sizeof(u16) }, + { &BG2HOFS , sizeof(u16) }, + { &BG2VOFS , sizeof(u16) }, + { &BG3HOFS , sizeof(u16) }, + { &BG3VOFS , sizeof(u16) }, + { &BG2PA , sizeof(u16) }, + { &BG2PB , sizeof(u16) }, + { &BG2PC , sizeof(u16) }, + { &BG2PD , sizeof(u16) }, + { &BG2X_L , sizeof(u16) }, + { &BG2X_H , sizeof(u16) }, + { &BG2Y_L , sizeof(u16) }, + { &BG2Y_H , sizeof(u16) }, + { &BG3PA , sizeof(u16) }, + { &BG3PB , sizeof(u16) }, + { &BG3PC , sizeof(u16) }, + { &BG3PD , sizeof(u16) }, + { &BG3X_L , sizeof(u16) }, + { &BG3X_H , sizeof(u16) }, + { &BG3Y_L , sizeof(u16) }, + { &BG3Y_H , sizeof(u16) }, + { &WIN0H , sizeof(u16) }, + { &WIN1H , sizeof(u16) }, + { &WIN0V , sizeof(u16) }, + { &WIN1V , sizeof(u16) }, + { &WININ , sizeof(u16) }, + { &WINOUT , sizeof(u16) }, + { &MOSAIC , sizeof(u16) }, + { &BLDMOD , sizeof(u16) }, + { &COLEV , sizeof(u16) }, + { &COLY , sizeof(u16) }, + { &DM0SAD_L , sizeof(u16) }, + { &DM0SAD_H , sizeof(u16) }, + { &DM0DAD_L , sizeof(u16) }, + { &DM0DAD_H , sizeof(u16) }, + { &DM0CNT_L , sizeof(u16) }, + { &DM0CNT_H , sizeof(u16) }, + { &DM1SAD_L , sizeof(u16) }, + { &DM1SAD_H , sizeof(u16) }, + { &DM1DAD_L , sizeof(u16) }, + { &DM1DAD_H , sizeof(u16) }, + { &DM1CNT_L , sizeof(u16) }, + { &DM1CNT_H , sizeof(u16) }, + { &DM2SAD_L , sizeof(u16) }, + { &DM2SAD_H , sizeof(u16) }, + { &DM2DAD_L , sizeof(u16) }, + { &DM2DAD_H , sizeof(u16) }, + { &DM2CNT_L , sizeof(u16) }, + { &DM2CNT_H , sizeof(u16) }, + { &DM3SAD_L , sizeof(u16) }, + { &DM3SAD_H , sizeof(u16) }, + { &DM3DAD_L , sizeof(u16) }, + { &DM3DAD_H , sizeof(u16) }, + { &DM3CNT_L , sizeof(u16) }, + { &DM3CNT_H , sizeof(u16) }, + { &TM0D , sizeof(u16) }, + { &TM0CNT , sizeof(u16) }, + { &TM1D , sizeof(u16) }, + { &TM1CNT , sizeof(u16) }, + { &TM2D , sizeof(u16) }, + { &TM2CNT , sizeof(u16) }, + { &TM3D , sizeof(u16) }, + { &TM3CNT , sizeof(u16) }, + { &P1 , sizeof(u16) }, + { &IE , sizeof(u16) }, + { &IF , sizeof(u16) }, + { &IME , sizeof(u16) }, + { &holdState, sizeof(bool) }, + { &holdType, sizeof(int) }, + { &lcdTicks, sizeof(int) }, + { &timer0On , sizeof(bool) }, + { &timer0Ticks , sizeof(int) }, + { &timer0Reload , sizeof(int) }, + { &timer0ClockReload , sizeof(int) }, + { &timer1On , sizeof(bool) }, + { &timer1Ticks , sizeof(int) }, + { &timer1Reload , sizeof(int) }, + { &timer1ClockReload , sizeof(int) }, + { &timer2On , sizeof(bool) }, + { &timer2Ticks , sizeof(int) }, + { &timer2Reload , sizeof(int) }, + { &timer2ClockReload , sizeof(int) }, + { &timer3On , sizeof(bool) }, + { &timer3Ticks , sizeof(int) }, + { &timer3Reload , sizeof(int) }, + { &timer3ClockReload , sizeof(int) }, + { &dma0Source , sizeof(u32) }, + { &dma0Dest , sizeof(u32) }, + { &dma1Source , sizeof(u32) }, + { &dma1Dest , sizeof(u32) }, + { &dma2Source , sizeof(u32) }, + { &dma2Dest , sizeof(u32) }, + { &dma3Source , sizeof(u32) }, + { &dma3Dest , sizeof(u32) }, + { &fxOn, sizeof(bool) }, + { &windowOn, sizeof(bool) }, + { &N_FLAG , sizeof(bool) }, + { &C_FLAG , sizeof(bool) }, + { &Z_FLAG , sizeof(bool) }, + { &V_FLAG , sizeof(bool) }, + { &armState , sizeof(bool) }, + { &armIrqEnable , sizeof(bool) }, + { &armNextPC , sizeof(u32) }, + { &armMode , sizeof(int) }, + { &saveType , sizeof(int) }, + { NULL, 0 } + }; + +//int cpuLoopTicks = 0; +int cpuSavedTicks = 0; + +#ifdef PROFILING +void cpuProfil(char *buf, int size, u32 lowPC, int scale) +{ + profilBuffer = buf; + profilSize = size; + profilLowPC = lowPC; + profilScale = scale; +} + +void cpuEnableProfiling(int hz) +{ + if(hz == 0) + hz = 100; + profilingTicks = profilingTicksReload = 16777216 / hz; + profSetHertz(hz); +} +#endif + +inline int CPUUpdateTicksAccess32(u32 address) +{ + return memoryWait32[(address>>24)&15]; +} + +inline int CPUUpdateTicksAccess16(u32 address) +{ + return memoryWait[(address>>24)&15]; +} + +inline int CPUUpdateTicksAccessSeq32(u32 address) +{ + return memoryWaitSeq32[(address>>24)&15]; +} + +inline int CPUUpdateTicksAccessSeq16(u32 address) +{ + return memoryWaitSeq[(address>>24)&15]; +} + +inline int CPUUpdateTicks() +{ + int cpuLoopTicks = lcdTicks; + + if(soundTicks < cpuLoopTicks) + cpuLoopTicks = soundTicks; + + if(timer0On && !(TM0CNT & 4) && (timer0Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer0Ticks; + } + if(timer1On && !(TM1CNT & 4) && (timer1Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer1Ticks; + } + if(timer2On && !(TM2CNT & 4) && (timer2Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer2Ticks; + } + if(timer3On && !(TM3CNT & 4) && (timer3Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer3Ticks; + } +#ifdef PROFILING + if(profilingTicksReload != 0) + { + if(profilingTicks < cpuLoopTicks) + { + cpuLoopTicks = profilingTicks; + } + } +#endif + cpuSavedTicks = cpuLoopTicks; + return cpuLoopTicks; +} + +void CPUUpdateWindow0() +{ + int x00 = WIN0H>>8; + int x01 = WIN0H & 255; + + if(x00 <= x01) + { + for(int i = 0; i < 240; i++) + { + gfxInWin0[i] = (i >= x00 && i < x01); + } + } + else + { + for(int i = 0; i < 240; i++) + { + gfxInWin0[i] = (i >= x00 || i < x01); + } + } +} + +void CPUUpdateWindow1() +{ + int x00 = WIN1H>>8; + int x01 = WIN1H & 255; + + if(x00 <= x01) + { + for(int i = 0; i < 240; i++) + { + gfxInWin1[i] = (i >= x00 && i < x01); + } + } + else + { + for(int i = 0; i < 240; i++) + { + gfxInWin1[i] = (i >= x00 || i < x01); + } + } +} + +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; + +#define CLEAR_ARRAY(a) \ + {\ + u32 *array = (a);\ + for(int i = 0; i < 240; i++) {\ + *array++ = 0x80000000;\ + }\ + }\ + +void CPUUpdateRenderBuffers(bool force) +{ + if(!(layerEnable & 0x0100) || force) + { + CLEAR_ARRAY(line0); + } + if(!(layerEnable & 0x0200) || force) + { + CLEAR_ARRAY(line1); + } + if(!(layerEnable & 0x0400) || force) + { + CLEAR_ARRAY(line2); + } + if(!(layerEnable & 0x0800) || force) + { + CLEAR_ARRAY(line3); + } +} + +static bool CPUWriteState(gzFile gzFile) +{ + utilWriteInt(gzFile, SAVE_GAME_VERSION); + + utilGzWrite(gzFile, &rom[0xa0], 16); + + utilWriteInt(gzFile, useBios); + + utilGzWrite(gzFile, ®[0], sizeof(reg)); + + utilWriteData(gzFile, saveGameStruct); + + // new to version 0.7.1 + utilWriteInt(gzFile, stopState); + // new to version 0.8 + utilWriteInt(gzFile, intState); + + utilGzWrite(gzFile, internalRAM, 0x8000); + utilGzWrite(gzFile, paletteRAM, 0x400); + utilGzWrite(gzFile, workRAM, 0x40000); + utilGzWrite(gzFile, vram, 0x20000); + utilGzWrite(gzFile, oam, 0x400); + utilGzWrite(gzFile, pix, 4*241*162); + utilGzWrite(gzFile, ioMem, 0x400); + + eepromSaveGame(gzFile); + flashSaveGame(gzFile); + soundSaveGame(gzFile); + + cheatsSaveGame(gzFile); + + // version 1.5 + rtcSaveGame(gzFile); + + return true; +} + +bool CPUWriteState(const char *file) +{ + gzFile gzFile = utilGzOpen(file, "wb"); + + if(gzFile == NULL) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), file); + return false; + } + + bool res = CPUWriteState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUWriteMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) + { + return false; + } + + bool res = CPUWriteState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +static bool CPUReadState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > SAVE_GAME_VERSION || version < SAVE_GAME_VERSION_1) + { + systemMessage(MSG_UNSUPPORTED_VBA_SGM, + N_("Unsupported VisualBoyAdvance save game version %d"), + version); + return false; + } + + u8 romname[17]; + + utilGzRead(gzFile, romname, 16); + + if(memcmp(&rom[0xa0], romname, 16) != 0) + { + romname[16]=0; + for(int i = 0; i < 16; i++) + if(romname[i] < 32) + romname[i] = 32; + systemMessage(MSG_CANNOT_LOAD_SGM, N_("Cannot load save game for %s"), romname); + return false; + } + + bool ub = utilReadInt(gzFile) ? true : false; + + if(ub != useBios) + { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + + utilGzRead(gzFile, ®[0], sizeof(reg)); + + utilReadData(gzFile, saveGameStruct); + + if(version < SAVE_GAME_VERSION_3) + stopState = false; + else + stopState = utilReadInt(gzFile) ? true : false; + + if(version < SAVE_GAME_VERSION_4) + intState = false; + else + intState = utilReadInt(gzFile) ? true : false; + + utilGzRead(gzFile, internalRAM, 0x8000); + utilGzRead(gzFile, paletteRAM, 0x400); + utilGzRead(gzFile, workRAM, 0x40000); + utilGzRead(gzFile, vram, 0x20000); + utilGzRead(gzFile, oam, 0x400); + if(version < SAVE_GAME_VERSION_6) + utilGzRead(gzFile, pix, 4*240*160); + else + utilGzRead(gzFile, pix, 4*241*162); + utilGzRead(gzFile, ioMem, 0x400); + + eepromReadGame(gzFile, version); + flashReadGame(gzFile, version); + soundReadGame(gzFile, version); + + if(version > SAVE_GAME_VERSION_1) + { + cheatsReadGame(gzFile); + } + if(version > SAVE_GAME_VERSION_6) + { + rtcReadGame(gzFile); + } + + if(version <= SAVE_GAME_VERSION_7) + { + u32 temp; +#define SWAP(a,b,c) \ + temp = (a);\ + (a) = (b)<<16|(c);\ + (b) = (temp) >> 16;\ + (c) = (temp) & 0xFFFF; + + SWAP(dma0Source, DM0SAD_H, DM0SAD_L); + SWAP(dma0Dest, DM0DAD_H, DM0DAD_L); + SWAP(dma1Source, DM1SAD_H, DM1SAD_L); + SWAP(dma1Dest, DM1DAD_H, DM1DAD_L); + SWAP(dma2Source, DM2SAD_H, DM2SAD_L); + SWAP(dma2Dest, DM2DAD_H, DM2DAD_L); + SWAP(dma3Source, DM3SAD_H, DM3SAD_L); + SWAP(dma3Dest, DM3DAD_H, DM3DAD_L); + } + + // set pointers! + layerEnable = layerSettings & DISPCNT; + + CPUUpdateRender(); + CPUUpdateRenderBuffers(true); + CPUUpdateWindow0(); + CPUUpdateWindow1(); + gbaSaveType = 0; + switch(saveType) + { + case 0: + cpuSaveGameFunc = flashSaveDecide; + break; + case 1: + cpuSaveGameFunc = sramWrite; + gbaSaveType = 1; + break; + case 2: + cpuSaveGameFunc = flashWrite; + gbaSaveType = 2; + break; + default: + systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, + N_("Unsupported save type %d"), saveType); + break; + } + if(eepromInUse) + gbaSaveType = 3; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + return true; +} + +bool CPUReadMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUReadState(const char * file) +{ + gzFile gzFile = utilGzOpen(file, "rb"); + + if(gzFile == NULL) + return false; + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUExportEepromFile(const char *fileName) +{ + if(eepromInUse) + { + FILE* file = gen_fopen(fileName, "wb"); + + if(!file) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + for(int i = 0; i < eepromSize;) + { + for(int j = 0; j < 8; j++) + { + if(gen_fwrite(&eepromData[i+7-j], 1, 1, file) != 1) + { + gen_fclose(file); + return false; + } + } + i += 8; + } + gen_fclose(file); + } + return true; +} + +bool CPUWriteBatteryFile(const char *fileName) +{ + if(gbaSaveType == 0) + { + if(eepromInUse) + gbaSaveType = 3; + else switch(saveType) + { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + + if(gbaSaveType) + { + FILE* file = gen_fopen(fileName, "wb"); + + if(!file) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + // only save if Flash/Sram in use or EEprom in use + if(gbaSaveType != 3) + { + if(gbaSaveType == 2) + { + if(gen_fwrite(flashSaveMemory, 1, flashSize, file) != (int)flashSize) + { + gen_fclose(file); + return false; + } + } + else + { + if(gen_fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) + { + gen_fclose(file); + return false; + } + } + } + else + { + if(gen_fwrite(eepromData, 1, eepromSize, file) != (int)eepromSize) + { + gen_fclose(file); + return false; + } + } + gen_fclose(file); + } + return true; +} + +bool CPUReadGSASnapshot(const char *fileName) +{ + int i; + FILE* file = gen_fopen(fileName, "rb"); + + if(!file) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // check file size to know what we should read + // gen_fseek(file, 0, SEEK_END); + + // long size = ftell(file); + gen_fseek(file, 0x0, SEEK_SET); + gen_fread(&i, 1, 4, file); + gen_fseek(file, i, SEEK_CUR); // Skip SharkPortSave + gen_fseek(file, 4, SEEK_CUR); // skip some sort of flag + gen_fread(&i, 1, 4, file); // name length + gen_fseek(file, i, SEEK_CUR); // skip name + gen_fread(&i, 1, 4, file); // desc length + gen_fseek(file, i, SEEK_CUR); // skip desc + gen_fread(&i, 1, 4, file); // notes length + gen_fseek(file, i, SEEK_CUR); // skip notes + int saveSize; + gen_fread(&saveSize, 1, 4, file); // read length + saveSize -= 0x1c; // remove header size + char buffer[17]; + char buffer2[17]; + gen_fread(buffer, 1, 16, file); + buffer[16] = 0; + for(i = 0; i < 16; i++) + if(buffer[i] < 32) + buffer[i] = 32; + memcpy(buffer2, &rom[0xa0], 16); + buffer2[16] = 0; + for(i = 0; i < 16; i++) + if(buffer2[i] < 32) + buffer2[i] = 32; + if(memcmp(buffer, buffer2, 16)) + { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + gen_fclose(file); + return false; + } + gen_fseek(file, 12, SEEK_CUR); // skip some flags + if(saveSize >= 65536) + { + if(gen_fread(flashSaveMemory, 1, saveSize, file) != (int)saveSize) + { + gen_fclose(file); + return false; + } + } + else + { + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + gen_fclose(file); + return false; + } + gen_fclose(file); + CPUReset(); + return true; +} + +bool CPUWriteGSASnapshot(const char *fileName, + const char *title, + const char *desc, + const char *notes) +{ + FILE* file = gen_fopen(fileName, "wb"); + + if(!file) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + u8 buffer[17]; + + utilPutDword(buffer, 0x0d); // SharkPortSave length + gen_fwrite(buffer, 1, 4, file); + gen_fwrite("SharkPortSave", 1, 0x0d, file); + utilPutDword(buffer, 0x000f0000); + gen_fwrite(buffer, 1, 4, file); // save type 0x000f0000 = GBA save + utilPutDword(buffer, strlen(title)); + gen_fwrite(buffer, 1, 4, file); // title length + gen_fwrite(title, 1, strlen(title), file); + utilPutDword(buffer, strlen(desc)); + gen_fwrite(buffer, 1, 4, file); // desc length + gen_fwrite(desc, 1, strlen(desc), file); + utilPutDword(buffer, strlen(notes)); + gen_fwrite(buffer, 1, 4, file); // notes length + gen_fwrite(notes, 1, strlen(notes), file); + int saveSize = 0x10000; + if(gbaSaveType == 2) + saveSize = flashSize; + int totalSize = saveSize + 0x1c; + + utilPutDword(buffer, totalSize); // length of remainder of save - CRC + gen_fwrite(buffer, 1, 4, file); + + char temp[0x2001c]; + memset(temp, 0, 28); + memcpy(temp, &rom[0xa0], 16); // copy internal name + temp[0x10] = rom[0xbe]; // reserved area (old checksum) + temp[0x11] = rom[0xbf]; // reserved area (old checksum) + temp[0x12] = rom[0xbd]; // complement check + temp[0x13] = rom[0xb0]; // maker + temp[0x14] = 1; // 1 save ? + memcpy(&temp[0x1c], flashSaveMemory, saveSize); // copy save + gen_fwrite(temp, 1, totalSize, file); // write save + header + u32 crc = 0; + + for(int i = 0; i < totalSize; i++) + { + crc += ((u32)temp[i] << (crc % 0x18)); + } + + utilPutDword(buffer, crc); + gen_fwrite(buffer, 1, 4, file); // CRC? + + gen_fclose(file); + return true; +} + +bool CPUImportEepromFile(const char *fileName) +{ + FILE* file = gen_fopen(fileName, "rb"); + + if(!file) + return false; + + + // check file size to know what we should read + gen_fseek(file, 0, SEEK_END); + + long size = ftell(file); + + gen_fseek(file, 0, SEEK_SET); + if(size == 512 || size == 0x2000) + { + if(gen_fread(eepromData, 1, size, file) != (int)size) + { + gen_fclose(file); + return false; + } + for(int i = 0; i < size;) + { + u8 tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + i += 4; + } + } + else + return false; + gen_fclose(file); + return true; +} + +bool CPUReadBatteryFile(const char *fileName) +{ + FILE* file = gen_fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + gen_fseek(file, 0, SEEK_END); + + long size = ftell(file); + + + gen_fseek(file, 0, SEEK_SET); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if(size == 512 || size == 0x2000) + { + if(gen_fread(eepromData, 1, size, file) != (int)size) + { + gen_fclose(file); + return false; + } + } + else + { + if(size == 0x20000) + { + if(gen_fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) + { + gen_fclose(file); + return false; + } + flashSetSize(0x20000); + } + else + { + if(gen_fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) + { + gen_fclose(file); + return false; + } + flashSetSize(0x10000); + } + } + gen_fclose(file); + return true; +} + +bool CPUWritePNGFile(const char *fileName) +{ + return utilWritePNGFile(fileName, 240, 160, pix); +} + +bool CPUWriteBMPFile(const char *fileName) +{ + return utilWriteBMPFile(fileName, 240, 160, pix); +} + +bool CPUIsZipFile(const char * file) +{ + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".zip") == 0) + return true; + } + } + + return false; +} + +bool CPUIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".elf") == 0) + return true; + if(_stricmp(p, ".mb") == 0) + { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool CPUIsGBABios(const char * file) +{ + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + } + } + + return false; +} + +bool CPUIsELF(const char *file) +{ + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".elf") == 0) + return true; + } + } + return false; +} + +void CPUCleanUp() +{ +#ifdef PROFILING + if(profilingTicksReload) + { + profCleanup(); + } +#endif + + if(rom != NULL) + { + free(rom); + rom = NULL; + } + + if(vram != NULL) + { + free(vram); + vram = NULL; + } + + if(paletteRAM != NULL) + { + free(paletteRAM); + paletteRAM = NULL; + } + + if(internalRAM != NULL) + { + free(internalRAM); + internalRAM = NULL; + } + + if(workRAM != NULL) + { + free(workRAM); + workRAM = NULL; + } + + if(bios != NULL) + { + free(bios); + bios = NULL; + } + + if(pix != NULL) + { + free(pix); + pix = NULL; + } + + if(oam != NULL) + { + free(oam); + oam = NULL; + } + + if(ioMem != NULL) + { + free(ioMem); + ioMem = NULL; + } + + elfCleanUp(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + emulating = 0; +} + +int CPULoadRom(const char *szFile) +{ + int size = 0x2000000; + + if(rom != NULL) + { + CPUCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + rom = (u8 *)malloc(0x2000000); + if(rom == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "ROM"); + return 0; + } + workRAM = (u8 *)calloc(1, 0x40000); + if(workRAM == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "WRAM"); + return 0; + } + + u8 *whereToLoad = rom; + if(cpuIsMultiBoot) + whereToLoad = workRAM; + + if(CPUIsELF(szFile)) + { + FILE* f = gen_fopen(szFile, "rb"); + if(!f) + { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), + szFile); + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + bool res = elfRead(szFile, size, f); + if(!res || size == 0) + { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + elfCleanUp(); + return 0; + } + } + else if(!utilLoad(szFile, + utilIsGBAImage, + whereToLoad, + size)) + { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + + u16 *temp = (u16 *)(rom+((size+1)&~1)); + int i; + for(i = (size+1)&~1; i < 0x2000000; i+=2) + { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + bios = (u8 *)calloc(1,0x4000); + if(bios == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "BIOS"); + CPUCleanUp(); + return 0; + } + internalRAM = (u8 *)calloc(1,0x8000); + if(internalRAM == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IRAM"); + CPUCleanUp(); + return 0; + } + paletteRAM = (u8 *)calloc(1,0x400); + if(paletteRAM == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PRAM"); + CPUCleanUp(); + return 0; + } + vram = (u8 *)calloc(1, 0x20000); + if(vram == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "VRAM"); + CPUCleanUp(); + return 0; + } + oam = (u8 *)calloc(1, 0x400); + if(oam == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "OAM"); + CPUCleanUp(); + return 0; + } + pix = (u8 *)calloc(1, 4 * 241 * 162); + if(pix == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PIX"); + CPUCleanUp(); + return 0; + } + ioMem = (u8 *)calloc(1, 0x400); + if(ioMem == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IO"); + CPUCleanUp(); + return 0; + } + + CPUUpdateRenderBuffers(true); + + return size; +} + +void CPUUpdateRender() +{ + switch(DISPCNT & 7) + { + case 0: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode0RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode0RenderLineNoWindow; + else + renderLine = mode0RenderLineAll; + break; + case 1: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode1RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode1RenderLineNoWindow; + else + renderLine = mode1RenderLineAll; + break; + case 2: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode2RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode2RenderLineNoWindow; + else + renderLine = mode2RenderLineAll; + break; + case 3: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode3RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode3RenderLineNoWindow; + else + renderLine = mode3RenderLineAll; + break; + case 4: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode4RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode4RenderLineNoWindow; + else + renderLine = mode4RenderLineAll; + break; + case 5: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode5RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode5RenderLineNoWindow; + else + renderLine = mode5RenderLineAll; + default: + break; + } +} + +void CPUUpdateCPSR() +{ + u32 CPSR = reg[16].I & 0x40; + if(N_FLAG) + CPSR |= 0x80000000; + if(Z_FLAG) + CPSR |= 0x40000000; + if(C_FLAG) + CPSR |= 0x20000000; + if(V_FLAG) + CPSR |= 0x10000000; + if(!armState) + CPSR |= 0x00000020; + if(!armIrqEnable) + CPSR |= 0x80; + CPSR |= (armMode & 0x1F); + reg[16].I = CPSR; +} + +void CPUUpdateFlags(bool breakLoop) +{ + u32 CPSR = reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true: false; + Z_FLAG = (CPSR & 0x40000000) ? true: false; + C_FLAG = (CPSR & 0x20000000) ? true: false; + V_FLAG = (CPSR & 0x10000000) ? true: false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if(breakLoop) + { + if(armIrqEnable && (IF & IE) && (IME & 1)) + { + CPU_BREAK_LOOP_2; + } + } +} + +void CPUUpdateFlags() +{ + CPUUpdateFlags(true); +} + +#ifdef WORDS_BIGENDIAN +static void CPUSwap(volatile u32 *a, volatile u32 *b) +{ + volatile u32 c = *b; + *b = *a; + *a = c; +} +#else +static void CPUSwap(u32 *a, u32 *b) +{ + u32 c = *b; + *b = *a; + *a = c; +} +#endif + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + // if(armMode == mode) + // return; + + CPUUpdateCPSR(); + + switch(armMode) + { + case 0x10: + case 0x1F: + reg[R13_USR].I = reg[13].I; + reg[R14_USR].I = reg[14].I; + reg[17].I = reg[16].I; + break; + case 0x11: + CPUSwap(®[R8_FIQ].I, ®[8].I); + CPUSwap(®[R9_FIQ].I, ®[9].I); + CPUSwap(®[R10_FIQ].I, ®[10].I); + CPUSwap(®[R11_FIQ].I, ®[11].I); + CPUSwap(®[R12_FIQ].I, ®[12].I); + reg[R13_FIQ].I = reg[13].I; + reg[R14_FIQ].I = reg[14].I; + reg[SPSR_FIQ].I = reg[17].I; + break; + case 0x12: + reg[R13_IRQ].I = reg[13].I; + reg[R14_IRQ].I = reg[14].I; + reg[SPSR_IRQ].I = reg[17].I; + break; + case 0x13: + reg[R13_SVC].I = reg[13].I; + reg[R14_SVC].I = reg[14].I; + reg[SPSR_SVC].I = reg[17].I; + break; + case 0x17: + reg[R13_ABT].I = reg[13].I; + reg[R14_ABT].I = reg[14].I; + reg[SPSR_ABT].I = reg[17].I; + break; + case 0x1b: + reg[R13_UND].I = reg[13].I; + reg[R14_UND].I = reg[14].I; + reg[SPSR_UND].I = reg[17].I; + break; + } + + u32 CPSR = reg[16].I; + u32 SPSR = reg[17].I; + + switch(mode) + { + case 0x10: + case 0x1F: + reg[13].I = reg[R13_USR].I; + reg[14].I = reg[R14_USR].I; + reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(®[8].I, ®[R8_FIQ].I); + CPUSwap(®[9].I, ®[R9_FIQ].I); + CPUSwap(®[10].I, ®[R10_FIQ].I); + CPUSwap(®[11].I, ®[R11_FIQ].I); + CPUSwap(®[12].I, ®[R12_FIQ].I); + reg[13].I = reg[R13_FIQ].I; + reg[14].I = reg[R14_FIQ].I; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_FIQ].I; + break; + case 0x12: + reg[13].I = reg[R13_IRQ].I; + reg[14].I = reg[R14_IRQ].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_IRQ].I; + break; + case 0x13: + reg[13].I = reg[R13_SVC].I; + reg[14].I = reg[R14_SVC].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_SVC].I; + break; + case 0x17: + reg[13].I = reg[R13_ABT].I; + reg[14].I = reg[R14_ABT].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_ABT].I; + break; + case 0x1b: + reg[13].I = reg[R13_UND].I; + reg[14].I = reg[R14_UND].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_UND].I; + break; + default: + systemMessage(MSG_UNSUPPORTED_ARM_MODE, N_("Unsupported ARM mode %02x"), mode); + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPUUpdateCPSR(); +} + +void CPUSwitchMode(int mode, bool saveState) +{ + CPUSwitchMode(mode, saveState, true); +} + +void CPUUndefinedException() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x1b, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + armNextPC = 0x04; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x13, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x08; + armState = true; + armIrqEnable = false; + armNextPC = 0x08; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt(int comment) +{ + static bool disableMessage = false; + if(armState) comment >>= 16; +#ifdef BKPT_SUPPORT + if(comment == 0xff) + { + extern void (*dbgOutput)(char *, u32); + dbgOutput(NULL, reg[0].I); + return; + } +#endif +#ifdef PROFILING + if(comment == 0xfe) + { + profStartup(reg[0].I, reg[1].I); + return; + } + if(comment == 0xfd) + { + profControl(reg[0].I); + return; + } + if(comment == 0xfc) + { + profCleanup(); + return; + } + if(comment == 0xfb) + { + profCount(); + return; + } +#endif + if(comment == 0xfa) + { + agbPrintFlush(); + return; + } +#ifdef SDL + if(comment == 0xf9) + { + emulating = 0; + CPU_BREAK_LOOP_2; + return; + } +#endif + if(useBios) + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + return; + } + // This would be correct, but it causes problems if uncommented + // else { + // biosProtected = 0xe3a02004; + // } + + switch(comment) + { + case 0x00: + BIOS_SoftReset(); + break; + case 0x01: + BIOS_RegisterRamReset(); + break; + case 0x02: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Halt: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + break; + case 0x03: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Stop: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + stopState = true; + break; + case 0x04: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x05: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("VBlankIntrWait: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x06: + CPUSoftwareInterrupt(); + break; + case 0x07: + CPUSoftwareInterrupt(); + break; + case 0x08: + BIOS_Sqrt(); + break; + case 0x09: + BIOS_ArcTan(); + break; + case 0x0A: + BIOS_ArcTan2(); + break; + case 0x0B: + BIOS_CpuSet(); + break; + case 0x0C: + BIOS_CpuFastSet(); + break; + case 0x0E: + BIOS_BgAffineSet(); + break; + case 0x0F: + BIOS_ObjAffineSet(); + break; + case 0x10: + BIOS_BitUnPack(); + break; + case 0x11: + BIOS_LZ77UnCompWram(); + break; + case 0x12: + BIOS_LZ77UnCompVram(); + break; + case 0x13: + BIOS_HuffUnComp(); + break; + case 0x14: + BIOS_RLUnCompWram(); + break; + case 0x15: + BIOS_RLUnCompVram(); + break; + case 0x16: + BIOS_Diff8bitUnFilterWram(); + break; + case 0x17: + BIOS_Diff8bitUnFilterVram(); + break; + case 0x18: + BIOS_Diff16bitUnFilter(); + break; + case 0x19: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, + VCOUNT); + } +#endif + if(reg[0].I) + systemSoundPause(); + else + systemSoundResume(); + break; + case 0x1F: + BIOS_MidiKey2Freq(); + break; + case 0x2A: + BIOS_SndDriverJmpTableCopy(); + // let it go, because we don't really emulate this function + default: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + if(!disableMessage) + { + systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, + N_("Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), + comment, + armMode ? armNextPC - 4: armNextPC - 2); + disableMessage = true; + } + break; + } +} + +void CPUCompareVCOUNT() +{ + if(VCOUNT == (DISPSTAT >> 8)) + { + DISPSTAT |= 4; + UPDATE_REG(0x04, DISPSTAT); + + if(DISPSTAT & 0x20) + { + IF |= 4; + UPDATE_REG(0x202, IF); + } + } + else + { + DISPSTAT &= 0xFFFB; + UPDATE_REG(0x4, DISPSTAT); + } +} + +void doDMA(u32 &s, u32 &d, u32 si, u32 di, u32 c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + + int sc = c; + + cpuDmaCount = c; + + if(transfer32) + { + s &= 0xFFFFFFFC; + if(s < 0x02000000 && (reg[15].I >> 24)) + { + while(c != 0) + { + CPUWriteMemory(d, 0); + d += di; + c--; + } + } + else + { + while(c != 0) + { + CPUWriteMemory(d, CPUReadMemory(s)); + d += di; + s += si; + c--; + } + } + } + else + { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if(s < 0x02000000 && (reg[15].I >> 24)) + { + while(c != 0) + { + CPUWriteHalfWord(d, 0); + d += di; + c--; + } + } + else + { + while(c != 0) + { + cpuDmaLast = CPUReadHalfWord(s); + CPUWriteHalfWord(d, cpuDmaLast); + d += di; + s += si; + c--; + } + } + } + + cpuDmaCount = 0; + + int sw = 1+memoryWaitSeq[sm & 15]; + int dw = 1+memoryWaitSeq[dm & 15]; + + int totalTicks = 0; + + if(transfer32) + { + if(!memory32[sm & 15]) + sw <<= 1; + if(!memory32[dm & 15]) + dw <<= 1; + } + + totalTicks = (sw+dw)*sc; + + cpuDmaTicksToUpdate += totalTicks; + + if(*extCpuLoopTicks >= 0) + { + CPU_BREAK_LOOP; + } +} + +void CPUCheckDMA(int reason, int dmamask) +{ + cpuDmaHack = 0; + // DMA 0 + if((DM0CNT_H & 0x8000) && (dmamask & 1)) + { + if(((DM0CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM0CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM0CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_DMA0) + { + int count = (DM0CNT_L ? DM0CNT_L : 0x4000) << 1; + if(DM0CNT_H & 0x0400) + count <<= 1; + log("DMA0: s=%08x d=%08x c=%04x count=%08x\n", dma0Source, dma0Dest, + DM0CNT_H, + count); + } +#endif + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + cpuDmaHack = 1; + if(DM0CNT_H & 0x4000) + { + IF |= 0x0100; + UPDATE_REG(0x202, IF); + } + + if(((DM0CNT_H >> 5) & 3) == 3) + { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if(!(DM0CNT_H & 0x0200) || (reason == 0)) + { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if((DM1CNT_H & 0x8000) && (dmamask & 2)) + { + if(((DM1CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM1CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM1CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_DMA1) + { + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + 16); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, 0, 4, + 0x0400); + } + else + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_DMA1) + { + int count = (DM1CNT_L ? DM1CNT_L : 0x4000) << 1; + if(DM1CNT_H & 0x0400) + count <<= 1; + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + count); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, destIncrement, + DM1CNT_L ? DM1CNT_L : 0x4000, + DM1CNT_H & 0x0400); + } + cpuDmaHack = 1; + + if(DM1CNT_H & 0x4000) + { + IF |= 0x0200; + UPDATE_REG(0x202, IF); + } + + if(((DM1CNT_H >> 5) & 3) == 3) + { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if(!(DM1CNT_H & 0x0200) || (reason == 0)) + { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if((DM2CNT_H & 0x8000) && (dmamask & 4)) + { + if(((DM2CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM2CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM2CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_DMA2) + { + int count = (4) << 2; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, 0, 4, + 0x0400); + } + else + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_DMA2) + { + int count = (DM2CNT_L ? DM2CNT_L : 0x4000) << 1; + if(DM2CNT_H & 0x0400) + count <<= 1; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, destIncrement, + DM2CNT_L ? DM2CNT_L : 0x4000, + DM2CNT_H & 0x0400); + } + cpuDmaHack = 1; + if(DM2CNT_H & 0x4000) + { + IF |= 0x0400; + UPDATE_REG(0x202, IF); + } + + if(((DM2CNT_H >> 5) & 3) == 3) + { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if(!(DM2CNT_H & 0x0200) || (reason == 0)) + { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if((DM3CNT_H & 0x8000) && (dmamask & 8)) + { + if(((DM3CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM3CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM3CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_DMA3) + { + int count = (DM3CNT_L ? DM3CNT_L : 0x10000) << 1; + if(DM3CNT_H & 0x0400) + count <<= 1; + log("DMA3: s=%08x d=%08x c=%04x count=%08x\n", dma3Source, dma3Dest, + DM3CNT_H, + count); + } +#endif + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + if(DM3CNT_H & 0x4000) + { + IF |= 0x0800; + UPDATE_REG(0x202, IF); + } + + if(((DM3CNT_H >> 5) & 3) == 3) + { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if(!(DM3CNT_H & 0x0200) || (reason == 0)) + { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } + cpuDmaHack = 0; +} + +void CPUUpdateRegister(u32 address, u16 value) +{ + switch(address) + { + case 0x00: + { + bool change = ((DISPCNT ^ value) & 0x80) ? true : false; + bool changeBG = ((DISPCNT ^ value) & 0x0F00) ? true : false; + DISPCNT = (value & 0xFFF7); + UPDATE_REG(0x00, DISPCNT); + layerEnable = layerSettings & value; + windowOn = (layerEnable & 0x6000) ? true : false; + if(change && !((value & 0x80))) + { + if(!(DISPSTAT & 1)) + { + lcdTicks = 960; + // VCOUNT = 0; + // UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + // (*renderLine)(); + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if(changeBG) + CPUUpdateRenderBuffers(false); + // CPUUpdateTicks(); + } + break; + case 0x04: + DISPSTAT = (value & 0xFF38) | (DISPSTAT & 7); + UPDATE_REG(0x04, DISPSTAT); + break; + case 0x06: + // not writable + break; + case 0x08: + BG0CNT = (value & 0xDFCF); + UPDATE_REG(0x08, BG0CNT); + break; + case 0x0A: + BG1CNT = (value & 0xDFCF); + UPDATE_REG(0x0A, BG1CNT); + break; + case 0x0C: + BG2CNT = (value & 0xFFCF); + UPDATE_REG(0x0C, BG2CNT); + break; + case 0x0E: + BG3CNT = (value & 0xFFCF); + UPDATE_REG(0x0E, BG3CNT); + break; + case 0x10: + BG0HOFS = value & 511; + UPDATE_REG(0x10, BG0HOFS); + break; + case 0x12: + BG0VOFS = value & 511; + UPDATE_REG(0x12, BG0VOFS); + break; + case 0x14: + BG1HOFS = value & 511; + UPDATE_REG(0x14, BG1HOFS); + break; + case 0x16: + BG1VOFS = value & 511; + UPDATE_REG(0x16, BG1VOFS); + break; + case 0x18: + BG2HOFS = value & 511; + UPDATE_REG(0x18, BG2HOFS); + break; + case 0x1A: + BG2VOFS = value & 511; + UPDATE_REG(0x1A, BG2VOFS); + break; + case 0x1C: + BG3HOFS = value & 511; + UPDATE_REG(0x1C, BG3HOFS); + break; + case 0x1E: + BG3VOFS = value & 511; + UPDATE_REG(0x1E, BG3VOFS); + break; + case 0x20: + BG2PA = value; + UPDATE_REG(0x20, BG2PA); + break; + case 0x22: + BG2PB = value; + UPDATE_REG(0x22, BG2PB); + break; + case 0x24: + BG2PC = value; + UPDATE_REG(0x24, BG2PC); + break; + case 0x26: + BG2PD = value; + UPDATE_REG(0x26, BG2PD); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: + BG3PA = value; + UPDATE_REG(0x30, BG3PA); + break; + case 0x32: + BG3PB = value; + UPDATE_REG(0x32, BG3PB); + break; + case 0x34: + BG3PC = value; + UPDATE_REG(0x34, BG3PC); + break; + case 0x36: + BG3PD = value; + UPDATE_REG(0x36, BG3PD); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + WIN0H = value; + UPDATE_REG(0x40, WIN0H); + CPUUpdateWindow0(); + break; + case 0x42: + WIN1H = value; + UPDATE_REG(0x42, WIN1H); + CPUUpdateWindow1(); + break; + case 0x44: + WIN0V = value; + UPDATE_REG(0x44, WIN0V); + break; + case 0x46: + WIN1V = value; + UPDATE_REG(0x46, WIN1V); + break; + case 0x48: + WININ = value & 0x3F3F; + UPDATE_REG(0x48, WININ); + break; + case 0x4A: + WINOUT = value & 0x3F3F; + UPDATE_REG(0x4A, WINOUT); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD>>6)&3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + soundEvent(address&0xFF, (u8)(value & 0xFF)); + soundEvent((address&0xFF)+1, (u8)(value>>8)); + break; + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent(address&0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if(start && (value & 0x8000)) + { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + } + break; + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if(start && (value & 0x8000)) + { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + } + break; + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if(start && (value & 0x8000)) + { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + } + break; + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if(start && (value & 0x8000)) + { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0,8); + } + } + break; + case 0x100: + timer0Reload = value; + break; + case 0x102: + timer0Ticks = timer0ClockReload = TIMER_TICKS[value & 3]; + if(!timer0On && (value & 0x80)) + { + // reload the counter + TM0D = timer0Reload; + if(timer0ClockReload == 1) + timer0Ticks = 0x10000 - TM0D; + UPDATE_REG(0x100, TM0D); + } + timer0On = value & 0x80 ? true : false; + TM0CNT = value & 0xC7; + UPDATE_REG(0x102, TM0CNT); + // CPUUpdateTicks(); + break; + case 0x104: + timer1Reload = value; + break; + case 0x106: + timer1Ticks = timer1ClockReload = TIMER_TICKS[value & 3]; + if(!timer1On && (value & 0x80)) + { + // reload the counter + TM1D = timer1Reload; + if(timer1ClockReload == 1) + timer1Ticks = 0x10000 - TM1D; + UPDATE_REG(0x104, TM1D); + } + timer1On = value & 0x80 ? true : false; + TM1CNT = value & 0xC7; + UPDATE_REG(0x106, TM1CNT); + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Ticks = timer2ClockReload = TIMER_TICKS[value & 3]; + if(!timer2On && (value & 0x80)) + { + // reload the counter + TM2D = timer2Reload; + if(timer2ClockReload == 1) + timer2Ticks = 0x10000 - TM2D; + UPDATE_REG(0x108, TM2D); + } + timer2On = value & 0x80 ? true : false; + TM2CNT = value & 0xC7; + UPDATE_REG(0x10A, TM2CNT); + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Ticks = timer3ClockReload = TIMER_TICKS[value & 3]; + if(!timer3On && (value & 0x80)) + { + // reload the counter + TM3D = timer3Reload; + if(timer3ClockReload == 1) + timer3Ticks = 0x10000 - TM3D; + UPDATE_REG(0x10C, TM3D); + } + timer3On = value & 0x80 ? true : false; + TM3CNT = value & 0xC7; + UPDATE_REG(0x10E, TM3CNT); + break; + case 0x128: + if(value & 0x80) + { + value &= 0xff7f; + if(value & 1 && (value & 0x4000)) + { + UPDATE_REG(0x12a, 0xFF); + IF |= 0x80; + UPDATE_REG(0x202, IF); + value &= 0x7f7f; + } + } + UPDATE_REG(0x128, value); + break; + case 0x130: + P1 |= (value & 0x3FF); + UPDATE_REG(0x130, P1); + break; + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + case 0x200: + IE = value & 0x3FFF; + UPDATE_REG(0x200, IE); + if((IME & 1) && (IF & IE) && armIrqEnable) + { + CPU_BREAK_LOOP_2; + } + break; + case 0x202: + IF ^= (value & IF); + UPDATE_REG(0x202, IF); + break; + case 0x204: + { + int i; + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + if(!speedHack) + { + memoryWait[0x08] = memoryWait[0x09] = gamepakWaitState[(value >> 2) & 7]; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = + gamepakWaitState0[(value >> 2) & 7]; + + memoryWait[0x0a] = memoryWait[0x0b] = gamepakWaitState[(value >> 5) & 7]; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = + gamepakWaitState1[(value >> 5) & 7]; + + memoryWait[0x0c] = memoryWait[0x0d] = gamepakWaitState[(value >> 8) & 7]; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = + gamepakWaitState2[(value >> 8) & 7]; + } + else + { + memoryWait[0x08] = memoryWait[0x09] = 4; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 2; + + memoryWait[0x0a] = memoryWait[0x0b] = 4; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 4; + + memoryWait[0x0c] = memoryWait[0x0d] = 4; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 8; + } + for(i = 0; i < 16; i++) + { + memoryWaitFetch32[i] = memoryWait32[i] = memoryWait[i] * + (memory32[i] ? 1 : 2); + memoryWaitFetch[i] = memoryWait[i]; + } + memoryWaitFetch32[3] += 1; + memoryWaitFetch32[2] += 3; + + if(value & 0x4000) + { + for(i = 8; i < 16; i++) + { + memoryWaitFetch32[i] = 2*cpuMemoryWait[i]; + memoryWaitFetch[i] = cpuMemoryWait[i]; + } + } + UPDATE_REG(0x204, value); + } + break; + case 0x208: + IME = value & 1; + UPDATE_REG(0x208, IME); + if((IME & 1) && (IF & IE) && armIrqEnable) + { + CPU_BREAK_LOOP_2; + } + break; + case 0x300: + if(value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address&0x3FE, value); + break; + } +} + +void CPUWriteHalfWord(u32 address, u16 value) +{ +#ifdef DEV_VERSION + if(address & 1) + { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaligned halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch(address >> 24) + { + case 2: +#ifdef SDL + if(*((u16 *)&freezeWorkRAM[address & 0x3FFFE])) + cheatsWriteHalfWord((u16 *)&workRAM[address & 0x3FFFE], + value, + *((u16 *)&freezeWorkRAM[address & 0x3FFFE])); + else +#endif + WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); + break; + case 3: +#ifdef SDL + if(*((u16 *)&freezeInternalRAM[address & 0x7ffe])) + cheatsWriteHalfWord((u16 *)&internalRAM[address & 0x7ffe], + value, + *((u16 *)&freezeInternalRAM[address & 0x7ffe])); + else +#endif + WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); + break; + case 4: + CPUUpdateRegister(address & 0x3fe, value); + break; + case 5: + WRITE16LE(((u16 *)&paletteRAM[address & 0x3fe]), value); + break; + case 6: + if(address & 0x10000) + WRITE16LE(((u16 *)&vram[address & 0x17ffe]), value); + else + WRITE16LE(((u16 *)&vram[address & 0x1fffe]), value); + break; + case 7: + WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); + break; + case 8: + case 9: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + { + if(!rtcWrite(address, value)) + goto unwritable; + } + else if(!agbPrintWrite(address, value)) goto unwritable; + break; + case 13: + if(cpuEEPROMEnabled) + { + eepromWrite(address, (u8)value); + break; + } + goto unwritable; + case 14: + if(!eepromInUse | cpuSramEnabled | cpuFlashEnabled) + { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + goto unwritable; + default: +unwritable: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) + { + log("Illegal halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +void CPUWriteByte(u32 address, u8 b) +{ + switch(address >> 24) + { + case 2: +#ifdef SDL + if(freezeWorkRAM[address & 0x3FFFF]) + cheatsWriteByte(&workRAM[address & 0x3FFFF], b); + else +#endif + workRAM[address & 0x3FFFF] = b; + break; + case 3: +#ifdef SDL + if(freezeInternalRAM[address & 0x7fff]) + cheatsWriteByte(&internalRAM[address & 0x7fff], b); + else +#endif + internalRAM[address & 0x7fff] = b; + break; + case 4: + switch(address & 0x3FF) + { + case 0x301: + if(b == 0x80) + stopState = true; + holdState = 1; + holdType = -1; + break; + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x68: + case 0x69: + case 0x6c: + case 0x6d: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x78: + case 0x79: + case 0x7c: + case 0x7d: + case 0x80: + case 0x81: + case 0x84: + case 0x85: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + soundEvent(address&0xFF, b); + break; + default: + // if(address & 1) { + // CPUWriteHalfWord(address-1, (CPUReadHalfWord(address-1)&0x00FF)|((int)b<<8)); + // } else + if(address & 1) + CPUUpdateRegister(address & 0x3fe, + ((READ16LE(((u16 *)&ioMem[address & 0x3fe]))) + & 0x00FF) | + b<<8); + else + CPUUpdateRegister(address & 0x3fe, + ((READ16LE(((u16 *)&ioMem[address & 0x3fe])) & 0xFF00) | b)); + } + break; + case 5: + // no need to switch + *((u16 *)&paletteRAM[address & 0x3FE]) = (b << 8) | b; + break; + case 6: + // no need to switch + if(address & 0x10000) + *((u16 *)&vram[address & 0x17FFE]) = (b << 8) | b; + else + *((u16 *)&vram[address & 0x1FFFE]) = (b << 8) | b; + break; + case 7: + // no need to switch + *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; + break; + case 13: + if(cpuEEPROMEnabled) + { + eepromWrite(address, b); + break; + } + goto unwritable; + case 14: + if(!eepromInUse | cpuSramEnabled | cpuFlashEnabled) + { + (*cpuSaveGameFunc)(address, b); + break; + } + // default + default: +unwritable: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) + { + log("Illegal byte write: %02x to %08x from %08x\n", + b, + address, + armMode ? armNextPC - 4 : armNextPC -2 ); + } +#endif + break; + } +} + +u8 cpuBitsSet[256]; +u8 cpuLowestBitSet[256]; + +void CPUInit(const char *biosFileName, bool useBiosFile) +{ +#ifdef WORDS_BIGENDIAN + if(!cpuBiosSwapped) + { + for(unsigned int i = 0; i < sizeof(myROM)/4; i++) + { + WRITE32LE(&myROM[i], myROM[i]); + } + cpuBiosSwapped = true; + } +#endif + gbaSaveType = 0; + eepromInUse = 0; + saveType = 0; + useBios = false; + + if(useBiosFile) + { +#if 0 + int size = 0x4000; + if(utilLoad(biosFileName, + CPUIsGBABios, + bios, + size)) + { + if(size == 0x4000) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BIOS file size")); + } +#else + FILE* bfil; + int res; + bfil = gen_fopen(biosFileName, "rb"); + if ( bfil == NULL ) + { + useBios = false; + } + else + { + res = gen_fread(bios, 1, 0x4000, bfil); + gen_fclose(bfil); + useBios = true; + } +#endif + } + + if(!useBios) + { + memcpy(bios, myROM, sizeof(myROM)); + } + + int i = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + for(i = 0; i < 256; i++) + { + int count = 0; + int j; + for(j = 0; j < 8; j++) + if(i & (1 << j)) + count++; + cpuBitsSet[i] = count; + + for(j = 0; j < 8; j++) + if(i & (1 << j)) + break; + cpuLowestBitSet[i] = j; + } + + for(i = 0; i < 0x400; i++) + ioReadable[i] = true; + for(i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for(i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for(i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for(i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for(i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for(i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for(i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for(i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for(i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for(i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for(i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for(i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for(i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for(i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for(i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for(i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + +#if 0 + *((u16 *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((u16 *)&rom[0x1fe209e]) = 0x4770; // BX LR +#endif + +} + +void CPUReset() +{ + if(gbaSaveType == 0) + { + if(eepromInUse) + gbaSaveType = 3; + else + switch(saveType) + { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + rtcReset(); + // clen registers + memset(®[0], 0, sizeof(reg)); + // clean OAM + memset(oam, 0, 0x400); + // clean palette + memset(paletteRAM, 0, 0x400); + // clean picture + memset(pix, 0, 4*160*240); + // clean vram + memset(vram, 0, 0x20000); + // clean io memory + memset(ioMem, 0, 0x400); + + DISPCNT = 0x0080; + DISPSTAT = 0x0000; + VCOUNT = 0x0000; + BG0CNT = 0x0000; + BG1CNT = 0x0000; + BG2CNT = 0x0000; + BG3CNT = 0x0000; + BG0HOFS = 0x0000; + BG0VOFS = 0x0000; + BG1HOFS = 0x0000; + BG1VOFS = 0x0000; + BG2HOFS = 0x0000; + BG2VOFS = 0x0000; + BG3HOFS = 0x0000; + BG3VOFS = 0x0000; + BG2PA = 0x0100; + BG2PB = 0x0000; + BG2PC = 0x0000; + BG2PD = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + BG3PA = 0x0100; + BG3PB = 0x0000; + BG3PC = 0x0000; + BG3PD = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + WIN0H = 0x0000; + WIN1H = 0x0000; + WIN0V = 0x0000; + WIN1V = 0x0000; + WININ = 0x0000; + WINOUT = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + TM0D = 0x0000; + TM0CNT = 0x0000; + TM1D = 0x0000; + TM1CNT = 0x0000; + TM2D = 0x0000; + TM2CNT = 0x0000; + TM3D = 0x0000; + TM3CNT = 0x0000; + P1 = 0x03FF; + IE = 0x0000; + IF = 0x0000; + IME = 0x0000; + + armMode = 0x1F; + + if(cpuIsMultiBoot) + { + reg[13].I = 0x03007F00; + reg[15].I = 0x02000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + else + { + if(useBios && !skipBios) + { + reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } + else + { + reg[13].I = 0x03007F00; + reg[15].I = 0x08000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, DISPCNT); + UPDATE_REG(0x20, BG2PA); + UPDATE_REG(0x26, BG2PD); + UPDATE_REG(0x30, BG3PA); + UPDATE_REG(0x36, BG3PD); + UPDATE_REG(0x130, P1); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + reg[16].I |= 0x40; + + CPUUpdateCPSR(); + + armNextPC = reg[15].I; + reg[15].I += 4; + + // reset internal state + holdState = false; + holdType = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + lcdTicks = 960; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + cpuSaveGameFunc = flashSaveDecide; + renderLine = mode0RenderLine; + fxOn = false; + windowOn = false; + frameCount = 0; + saveType = 0; + layerEnable = DISPCNT & layerSettings; + + CPUUpdateRenderBuffers(true); + + for(int i = 0; i < 256; i++) + { + map[i].address = (u8 *)&dummyAddress; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if(!useBios) + { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else + BIOS_RegisterRamReset(0xff); + } + else + { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + } + + switch(cpuSaveType) + { + case 0: // automatic + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + break; + case 1: // EEPROM + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + break; + case 2: // SRAM + cpuSramEnabled = true; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = sramWrite; + break; + case 3: // FLASH + cpuSramEnabled = false; + cpuFlashEnabled = true; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = flashWrite; + break; + case 4: // EEPROM+Sensor + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + break; + case 5: // NONE + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + break; + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + lastTime = systemGetClock(); +} + +void CPUInterrupt() +{ + u32 PC = reg[15].I; + bool savedState = armState; + CPUSwitchMode(0x12, true, false); + reg[14].I = PC; + if(!savedState) + reg[14].I += 2; + reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + armNextPC = reg[15].I; + reg[15].I += 4; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +void log(const char *defaultMsg, ...) +{ +#if 0 + char buffer[2048]; + va_list valist; + + va_start(valist, defaultMsg); + vsprintf(buffer, defaultMsg, valist); + + if(out == NULL) + { + out = gen_fopen("trace.log","w"); + } + + fputs(buffer, out); + + va_end(valist); +#endif +} + +void CPULoop(int ticks) +{ + int clockTicks; + int cpuLoopTicks = 0; + int timerOverflow = 0; + // variables used by the CPU core + + extCpuLoopTicks = &cpuLoopTicks; + extClockTicks = &clockTicks; + extTicks = &ticks; + + cpuLoopTicks = CPUUpdateTicks(); + if(cpuLoopTicks > ticks) + { + cpuLoopTicks = ticks; + cpuSavedTicks = ticks; + } + + if(intState) + { + cpuLoopTicks = 5; + cpuSavedTicks = 5; + } + + for(;;) + { +#ifndef FINAL_VERSION + if(systemDebug) + { + if(systemDebug >= 10 && !holdState) + { + CPUUpdateCPSR(); + sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + reg[0].I, reg[1].I, reg[2].I, reg[3].I, reg[4].I, reg[5].I, + reg[6].I, reg[7].I, reg[8].I, reg[9].I, reg[10].I, reg[11].I, + reg[12].I, reg[13].I, reg[14].I, reg[15].I, reg[16].I, + reg[17].I); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } + else if(!holdState) + { + sprintf(buffer, "PC=%08x\n", armNextPC); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } + } +#endif + + if(!holdState) + { + if(armState) + { +#include "arm-new.h" + } + else + { +#include "thumb.h" + } + } + else + { + clockTicks = lcdTicks; + + if(soundTicks < clockTicks) + clockTicks = soundTicks; + + if(timer0On && (timer0Ticks < clockTicks)) + { + clockTicks = timer0Ticks; + } + if(timer1On && (timer1Ticks < clockTicks)) + { + clockTicks = timer1Ticks; + } + if(timer2On && (timer2Ticks < clockTicks)) + { + clockTicks = timer2Ticks; + } + if(timer3On && (timer3Ticks < clockTicks)) + { + clockTicks = timer3Ticks; + } +#ifdef PROFILING + if(profilingTicksReload != 0) + { + if(profilingTicks < clockTicks) + { + clockTicks = profilingTicks; + } + } +#endif + } + + cpuLoopTicks -= clockTicks; + if((cpuLoopTicks <= 0)) + { + if(cpuSavedTicks) + { + clockTicks = cpuSavedTicks;// + cpuLoopTicks; + } + cpuDmaTicksToUpdate = -cpuLoopTicks; + +updateLoop: + lcdTicks -= clockTicks; + + if(lcdTicks <= 0) + { + if(DISPSTAT & 1) + { // V-BLANK + // if in V-Blank mode, keep computing... + if(DISPSTAT & 2) + { + lcdTicks += 960; + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + else + { + lcdTicks += 272; + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 16) + { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + + if(VCOUNT >= 228) + { + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + VCOUNT = 0; + UPDATE_REG(0x06, VCOUNT); + CPUCompareVCOUNT(); + } + } + else + { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + + if(DISPSTAT & 2) + { + // if in H-Blank, leave it and move to drawing mode + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + + lcdTicks += (960); + DISPSTAT &= 0xFFFD; + if(VCOUNT == 160) + { + count++; + systemFrame(); + + if((count % 10) == 0) + { + system10Frames(60); + } + if(count == 60) + { + u32 time = systemGetClock(); + if(time != lastTime) + { + u32 t = 100000/(time - lastTime); + systemShowSpeed(t); + } + else + systemShowSpeed(0); + lastTime = time; + count = 0; + } + u32 joy = 0; + // update joystick information + if(systemReadJoypads()) + // read default joystick + joy = systemReadJoypad(-1); + P1 = 0x03FF ^ (joy & 0x3FF); + if(cpuEEPROMSensorEnabled) + systemUpdateMotionSensor(); + UPDATE_REG(0x130, P1); + u16 P1CNT = READ16LE(((u16 *)&ioMem[0x132])); + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if((P1CNT & 0x4000) || stopState) + { + u16 p1 = (0x3FF ^ P1) & 0x3FF; + if(P1CNT & 0x8000) + { + if(p1 == (P1CNT & 0x3FF)) + { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + else + { + if(p1 & P1CNT) + { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + } + + u32 ext = (joy >> 10); + int cheatTicks = 0; + if(cheatsEnabled) + cheatsCheckKeys(P1^0x3FF, ext); + cpuDmaTicksToUpdate += cheatTicks; + speedup = (ext & 1) ? true : false; + capture = (ext & 2) ? true : false; + + if(capture && !capturePrevious) + { + captureNumber++; + systemScreenCapture(captureNumber); + } + capturePrevious = capture; + + DISPSTAT |= 1; + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 0x0008) + { + IF |= 1; + UPDATE_REG(0x202, IF); + } + CPUCheckDMA(1, 0x0f); + if(frameCount >= framesToSkip) + { + systemDrawScreen(); + frameCount = 0; + } + else + frameCount++; + if(systemPauseOnFrame()) + ticks = 0; + } + + UPDATE_REG(0x04, DISPSTAT); + + CPUCompareVCOUNT(); + } + else + { + if(frameCount >= framesToSkip) + { + (*renderLine)(); + + switch(systemColorDepth) + { + case 16: + { + u16 *dest = (u16 *)pix + 242 * (VCOUNT+1); + for(int x = 0; x < 240;) + { + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + } + // for filters that read past the screen + *dest++ = 0; + } + break; + case 24: + { + u8 *dest = (u8 *)pix + 240 * VCOUNT * 3; + for(int x = 0; x < 240;) + { + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + } + } + break; + case 32: + { + u32 *dest = (u32 *)pix + 241 * (VCOUNT+1); + for(int x = 0; x < 240; ) + { + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + } + } + break; + } + } + // entering H-Blank + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + lcdTicks += 272; + CPUCheckDMA(2, 0x0f); + if(DISPSTAT & 16) + { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + } + } + + if(!stopState) + { + if(timer0On) + { + if(timer0ClockReload == 1) + { + u32 tm0d = TM0D + clockTicks; + if(tm0d > 0xffff) + { + tm0d += timer0Reload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(TM0CNT & 0x40) + { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + TM0D = tm0d; + timer0Ticks = 0x10000 - TM0D; + UPDATE_REG(0x100, TM0D); + } + else + { + timer0Ticks -= clockTicks; + if(timer0Ticks <= 0) + { + timer0Ticks += timer0ClockReload; + TM0D++; + if(TM0D == 0) + { + TM0D = timer0Reload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(TM0CNT & 0x40) + { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x100, TM0D); + } + } + } + + if(timer1On) + { + if(TM1CNT & 4) + { + if(timerOverflow & 1) + { + TM1D++; + if(TM1D == 0) + { + TM1D += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) + { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } + else + { + if(timer1ClockReload == 1) + { + u32 tm1d = TM1D + clockTicks; + if(tm1d > 0xffff) + { + tm1d += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) + { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + TM1D = tm1d; + timer1Ticks = 0x10000 - TM1D; + UPDATE_REG(0x104, TM1D); + } + else + { + timer1Ticks -= clockTicks; + if(timer1Ticks <= 0) + { + timer1Ticks += timer1ClockReload; + TM1D++; + + if(TM1D == 0) + { + TM1D = timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) + { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } + } + } + + if(timer2On) + { + if(TM2CNT & 4) + { + if(timerOverflow & 2) + { + TM2D++; + if(TM2D == 0) + { + TM2D += timer2Reload; + timerOverflow |= 4; + if(TM2CNT & 0x40) + { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } + else + { + if(timer2ClockReload == 1) + { + u32 tm2d = TM2D + clockTicks; + if(tm2d > 0xffff) + { + tm2d += timer2Reload; + timerOverflow |= 4; + if(TM2CNT & 0x40) + { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + TM2D = tm2d; + timer2Ticks = 0x10000 - TM2D; + UPDATE_REG(0x108, TM2D); + } + else + { + timer2Ticks -= clockTicks; + if(timer2Ticks <= 0) + { + timer2Ticks += timer2ClockReload; + TM2D++; + + if(TM2D == 0) + { + TM2D = timer2Reload; + timerOverflow |= 4; + if(TM2CNT & 0x40) + { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } + } + } + + if(timer3On) + { + if(TM3CNT & 4) + { + if(timerOverflow & 4) + { + TM3D++; + if(TM3D == 0) + { + TM3D += timer3Reload; + if(TM3CNT & 0x40) + { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10c, TM3D); + } + } + else + { + if(timer3ClockReload == 1) + { + u32 tm3d = TM3D + clockTicks; + if(tm3d > 0xffff) + { + tm3d += timer3Reload; + if(TM3CNT & 0x40) + { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + TM3D = tm3d; + timer3Ticks = 0x10000 - TM3D; + UPDATE_REG(0x10C, TM3D); + } + else + { + timer3Ticks -= clockTicks; + if(timer3Ticks <= 0) + { + timer3Ticks += timer3ClockReload; + TM3D++; + + if(TM3D == 0) + { + TM3D = timer3Reload; + if(TM3CNT & 0x40) + { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10C, TM3D); + } + } + } + } + } + // we shouldn't be doing sound in stop state, but we lose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if(soundTicks <= 0) + { + soundTick(); + soundTicks += SOUND_CLOCK_TICKS; + } + timerOverflow = 0; + +#ifdef PROFILING + profilingTicks -= clockTicks; + if(profilingTicks <= 0) + { + profilingTicks += profilingTicksReload; + if(profilBuffer && profilSize) + { + u16 *b = (u16 *)profilBuffer; + int pc = ((reg[15].I - profilLowPC) * profilScale)/0x10000; + if(pc >= 0 && pc < profilSize) + { + b[pc]++; + } + } + } +#endif + + ticks -= clockTicks; + + cpuLoopTicks = CPUUpdateTicks(); + + if(cpuDmaTicksToUpdate > 0) + { + clockTicks = cpuSavedTicks; + if(clockTicks > cpuDmaTicksToUpdate) + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if(cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + goto updateLoop; + } + + if(IF && (IME & 1) && armIrqEnable) + { + int res = IF & IE; + if(stopState) + res &= 0x3080; + if(res) + { + if(intState) + { + CPUInterrupt(); + intState = false; + if(holdState) + { + holdState = false; + stopState = false; + } + } + else + { + if(!holdState) + { + intState = true; + cpuLoopTicks = 5; + cpuSavedTicks = 5; + } + else + { + CPUInterrupt(); + if(holdState) + { + holdState = false; + stopState = false; + } + } + } + } + } + + if(ticks <= 0) + break; + } + } +} + +struct EmulatedSystem GBASystem = + { + // emuMain + CPULoop, + // emuReset + CPUReset, + // emuCleanUp + CPUCleanUp, + // emuReadBattery + CPUReadBatteryFile, + // emuWriteBattery + CPUWriteBatteryFile, + // emuReadState + CPUReadState, + // emuWriteState + CPUWriteState, + // emuReadMemState + CPUReadMemState, + // emuWriteMemState + CPUWriteMemState, + // emuWritePNG + CPUWritePNGFile, + // emuWriteBMP + CPUWriteBMPFile, + // emuUpdateCPSR + CPUUpdateCPSR, + // emuHasDebugger + true, + // emuCount +#ifdef FINAL_VERSION + 250000 +#else + 5000 +#endif + }; diff --git a/src/GBA.h b/src/GBA.h new file mode 100644 index 0000000..46169e6 --- /dev/null +++ b/src/GBA.h @@ -0,0 +1,153 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GBA_H +#define VBA_GBA_H + +#include "System.h" + +#define SAVE_GAME_VERSION_1 1 +#define SAVE_GAME_VERSION_2 2 +#define SAVE_GAME_VERSION_3 3 +#define SAVE_GAME_VERSION_4 4 +#define SAVE_GAME_VERSION_5 5 +#define SAVE_GAME_VERSION_6 6 +#define SAVE_GAME_VERSION_7 7 +#define SAVE_GAME_VERSION_8 8 +#define SAVE_GAME_VERSION SAVE_GAME_VERSION_8 + +typedef struct + { + u8 *address; + u32 mask; + } +memoryMap; + +typedef union { + struct + { +#ifdef WORDS_BIGENDIAN + u8 B3; + u8 B2; + u8 B1; + u8 B0; +#else + u8 B0; + u8 B1; + u8 B2; + u8 B3; +#endif + } + B; + struct + { +#ifdef WORDS_BIGENDIAN + u16 W1; + u16 W0; +#else + u16 W0; + u16 W1; +#endif + } + W; +#ifdef WORDS_BIGENDIAN + volatile u32 I; +#else + u32 I; +#endif + } reg_pair; + +#ifndef NO_GBA_MAP +extern memoryMap map[256]; +#endif + +extern reg_pair reg[45]; +extern u8 biosProtected[4]; + +extern bool N_FLAG; +extern bool Z_FLAG; +extern bool C_FLAG; +extern bool V_FLAG; +extern bool armIrqEnable; +extern bool armState; +extern int armMode; +extern void (*cpuSaveGameFunc)(u32,u8); + +extern bool freezeWorkRAM[0x40000]; +extern bool freezeInternalRAM[0x8000]; +extern bool CPUReadGSASnapshot(const char *); +extern bool CPUWriteGSASnapshot(const char *, const char *, const char *, const char *); +extern bool CPUWriteBatteryFile(const char *); +extern bool CPUReadBatteryFile(const char *); +extern bool CPUExportEepromFile(const char *); +extern bool CPUImportEepromFile(const char *); +extern bool CPUWritePNGFile(const char *); +extern bool CPUWriteBMPFile(const char *); +extern void CPUCleanUp(); +extern void CPUUpdateRender(); +extern bool CPUReadMemState(char *, int); +extern bool CPUReadState(const char *); +extern bool CPUWriteMemState(char *, int); +extern bool CPUWriteState(const char *); +extern int CPULoadRom(const char *); +extern void CPUUpdateRegister(u32, u16); +extern void CPUWriteHalfWord(u32, u16); +extern void CPUWriteByte(u32, u8); +extern void CPUInit(const char *,bool); +extern void CPUReset(); +extern void CPULoop(int); +extern void CPUCheckDMA(int,int); +extern bool CPUIsGBAImage(const char *); +extern bool CPUIsZipFile(const char *); +#ifdef PROFILING +extern void cpuProfil(char *buffer, int, u32, int); +extern void cpuEnableProfiling(int hz); +#endif + +extern struct EmulatedSystem GBASystem; + +#define R13_IRQ 18 +#define R14_IRQ 19 +#define SPSR_IRQ 20 +#define R13_USR 26 +#define R14_USR 27 +#define R13_SVC 28 +#define R14_SVC 29 +#define SPSR_SVC 30 +#define R13_ABT 31 +#define R14_ABT 32 +#define SPSR_ABT 33 +#define R13_UND 34 +#define R14_UND 35 +#define SPSR_UND 36 +#define R8_FIQ 37 +#define R9_FIQ 38 +#define R10_FIQ 39 +#define R11_FIQ 40 +#define R12_FIQ 41 +#define R13_FIQ 42 +#define R14_FIQ 43 +#define SPSR_FIQ 44 + +#include "Cheats.h" +#include "Globals.h" +#include "EEprom.h" +#include "Flash.h" + +#endif //VBA_GBA_H diff --git a/src/GBAinline.h b/src/GBAinline.h new file mode 100644 index 0000000..f4fc4c7 --- /dev/null +++ b/src/GBAinline.h @@ -0,0 +1,556 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GBAinline_H +#define VBA_GBAinline_H + +#include "System.h" +#include "Port.h" +#include "RTC.h" +#include "vmmem.h" + +extern bool cpuSramEnabled; +extern bool cpuFlashEnabled; +extern bool cpuEEPROMEnabled; +extern bool cpuEEPROMSensorEnabled; + +#define VM_USED 1 + +#define CPUReadByteQuickDef(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define CPUReadHalfWordQuickDef(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define CPUReadMemoryQuickDef(addr) \ + READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +u8 inline CPUReadByteQuick( u32 addr ) +{ + switch(addr >> 24 ) + { + case 8: + case 9: + case 10: + case 12: + return VMRead8( addr & 0x1FFFFFF ); + + default: + return CPUReadByteQuickDef(addr); + } + + return 0; +} + +u16 inline CPUReadHalfWordQuick( u32 addr ) +{ + switch(addr >> 24) + { + case 8: + case 9: + case 10: + case 12: + return VMRead16( addr & 0x1FFFFFF ); + default: + return CPUReadHalfWordQuickDef(addr); + } + + return 0; +} + +u32 inline CPUReadMemoryQuick( u32 addr ) +{ + switch(addr >> 24) + { + case 8: + case 9: + case 10: + case 12: + return VMRead32( addr & 0x1FFFFFF ); + default: + return CPUReadMemoryQuickDef(addr); + } + + return 0; +} + +inline u32 CPUReadMemory(u32 address) +{ + +#ifdef DEV_VERSION + if(address & 3) + { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaligned word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + switch(address >> 24) + { + case 0: + if(reg[15].I >> 24) + { + if(address < 0x4000) + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + value = READ32LE(((u32 *)&biosProtected)); + } + else goto unreadable; + } + else + value = READ32LE(((u32 *)&bios[address & 0x3FFC])); + break; + case 2: + value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); + break; + case 3: + value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fc]) + { + if(ioReadable[(address & 0x3fc) + 2]) + value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); + else + value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); + } + else goto unreadable; + break; + case 5: + value = READ32LE(((u32 *)&paletteRAM[address & 0x3fC])); + break; + case 6: + value = READ32LE(((u32 *)&vram[address & 0x1fffc])); + break; + case 7: + value = READ32LE(((u32 *)&oam[address & 0x3FC])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + /** Need NGC VM here **/ + //value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); + value = VMRead32( address & 0x1FFFFFC ); + break; + case 13: + if(cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if(cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: +unreadable: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + // if(ioMem[0x205] & 0x40) { + if(armState) + { +#if VM_USED + value = CPUReadMemoryQuick(reg[15].I); +#else + value = CPUReadMemoryQuickDef(reg[15].I); +#endif + //value = VMRead32(reg[15].I); + } + else + { +#if VM_USED + value = CPUReadHalfWordQuick(reg[15].I) | + CPUReadHalfWordQuick(reg[15].I) << 16; +#else + value = CPUReadHalfWordQuickDef(reg[15].I) | + CPUReadHalfWordQuickDef(reg[15].I) << 16; +#endif + //value = VMRead16(reg[15].I) | VMRead16(reg[15].I) << 16; + } + // } else { + // value = *((u32 *)&bios[address & 0x3ffc]); + // } + // return 0xFFFFFFFF; + } + + if(address & 3) + { +#ifdef C_CORE + int shift = (address & 3) << 3; + value = (value >> shift) | (value << (32 - shift)); +#else +#ifdef __GNUC__ + asm("and $3, %%ecx;" + "shl $3 ,%%ecx;" + "ror %%cl, %0" + : "=r" (value) + : "r" (value), "c" (address)); +#else + __asm { + mov ecx, address; + and ecx, 3; + shl ecx, 3; + ror [dword ptr value], cl; + } +#endif +#endif + } + return value; +} + +extern u32 myROM[]; + +inline u32 CPUReadHalfWord(u32 address) +{ +#ifdef DEV_VERSION + if(address & 1) + { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaligned halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + + switch(address >> 24) + { + case 0: + if (reg[15].I >> 24) + { + if(address < 0x4000) + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + value = READ16LE(((u16 *)&biosProtected[address&2])); + } + else goto unreadable; + } + else + value = READ16LE(((u16 *)&bios[address & 0x3FFE])); + break; + case 2: + value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); + break; + case 3: + value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fe]) + value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); + else goto unreadable; + break; + case 5: + value = READ16LE(((u16 *)&paletteRAM[address & 0x3fe])); + break; + case 6: + value = READ16LE(((u16 *)&vram[address & 0x1fffe])); + break; + case 7: + value = READ16LE(((u16 *)&oam[address & 0x3fe])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + value = rtcRead(address); + else + /** Need NGC VM Here **/ + //value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); + value = VMRead16( address & 0x1FFFFFE ); + break; + case 13: + if(cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if(cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: +unreadable: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + extern bool cpuDmaHack; + extern u32 cpuDmaLast; + extern int cpuDmaCount; + if(cpuDmaHack && cpuDmaCount) + { + value = (u16)cpuDmaLast; + } + else + { + if(armState) + { +#if VM_USED + value = CPUReadHalfWordQuick(reg[15].I + (address & 2)); +#else + value = CPUReadHalfWordQuickDef(reg[15].I + (address & 2)); +#endif + //value = VMRead16(reg[15].I + (address & 2)); + } + else + { +#if VM_USED + value = CPUReadHalfWordQuick(reg[15].I); +#else + value = CPUReadHalfWordQuickDef(reg[15].I); +#endif + //value = VMRead16(reg[15].I); + } + } + // return value; + // if(address & 1) + // value = (value >> 8) | ((value & 0xFF) << 24); + // return 0xFFFF; + break; + } + + if(address & 1) + { + value = (value >> 8) | (value << 24); + } + + return value; +} + +inline u16 CPUReadHalfWordSigned(u32 address) +{ + u16 value = CPUReadHalfWord(address); + if((address & 1)) + value = (s8)value; + return value; +} + +inline u8 CPUReadByte(u32 address) +{ + switch(address >> 24) + { + case 0: + if (reg[15].I >> 24) + { + if(address < 0x4000) + { +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + return biosProtected[address & 3]; + } + else goto unreadable; + } + return bios[address & 0x3FFF]; + case 2: + return workRAM[address & 0x3FFFF]; + case 3: + return internalRAM[address & 0x7fff]; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3ff]) + return ioMem[address & 0x3ff]; + else goto unreadable; + case 5: + return paletteRAM[address & 0x3ff]; + case 6: + return vram[address & 0x1ffff]; + case 7: + return oam[address & 0x3ff]; + case 8: + case 9: + case 10: + case 11: + case 12: + /** Need NGC VM Here **/ + //return rom[address & 0x1FFFFFF]; + return VMRead8( address & 0x1FFFFFF ); + case 13: + if(cpuEEPROMEnabled) + return eepromRead(address); + goto unreadable; + case 14: + if(cpuSramEnabled | cpuFlashEnabled) + return flashRead(address); + if(cpuEEPROMSensorEnabled) + { + switch(address & 0x00008f00) + { + case 0x8200: + return systemGetSensorX() & 255; + case 0x8300: + return (systemGetSensorX() >> 8)|0x80; + case 0x8400: + return systemGetSensorY() & 255; + case 0x8500: + return systemGetSensorY() >> 8; + } + } + // default + default: +unreadable: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + if(armState) + { +#if VM_USED + return CPUReadByteQuick(reg[15].I+(address & 3)); +#else + return CPUReadByteQuickDef(reg[15].I+(address & 3)); +#endif + //return VMRead8(reg[15].I+(address & 3)); + } + else + { +#if VM_USED + return CPUReadByteQuick(reg[15].I+(address & 1)); +#else + return CPUReadByteQuickDef(reg[15].I+(address & 1)); +#endif + //return VMRead8(reg[15].I+(address & 1)); + } + // return 0xFF; + break; + } +} + +inline void CPUWriteMemory(u32 address, u32 value) +{ +#ifdef DEV_VERSION + if(address & 3) + { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaliagned word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch(address >> 24) + { + case 0x02: +#ifdef SDL + if(*((u32 *)&freezeWorkRAM[address & 0x3FFFC])) + cheatsWriteMemory((u32 *)&workRAM[address & 0x3FFFC], + value, + *((u32 *)&freezeWorkRAM[address & 0x3FFFC])); + else +#endif + WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); + break; + case 0x03: +#ifdef SDL + if(*((u32 *)&freezeInternalRAM[address & 0x7ffc])) + cheatsWriteMemory((u32 *)&internalRAM[address & 0x7FFC], + value, + *((u32 *)&freezeInternalRAM[address & 0x7ffc])); + else +#endif + WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); + break; + case 0x04: + CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); + CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); + break; + case 0x05: + WRITE32LE(((u32 *)&paletteRAM[address & 0x3FC]), value); + break; + case 0x06: + if(address & 0x10000) + WRITE32LE(((u32 *)&vram[address & 0x17ffc]), value); + else + WRITE32LE(((u32 *)&vram[address & 0x1fffc]), value); + break; + case 0x07: + WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); + break; + case 0x0D: + if(cpuEEPROMEnabled) + { + eepromWrite(address, value); + break; + } + goto unwritable; + case 0x0E: + if(!eepromInUse | cpuSramEnabled | cpuFlashEnabled) + { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + // default + default: +unwritable: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) + { + log("Illegal word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +#endif //VBA_GBAinline_H diff --git a/src/Gfx.cpp b/src/Gfx.cpp new file mode 100644 index 0000000..c4ba850 --- /dev/null +++ b/src/Gfx.cpp @@ -0,0 +1,47 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "System.h" + +int coeff[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + + +u32 line0[240]; +u32 line1[240]; +u32 line2[240]; +u32 line3[240]; +u32 lineOBJ[240]; +u32 lineOBJWin[240]; +u32 lineMix[240]; +bool gfxInWin0[240]; +bool gfxInWin1[240]; + +int gfxBG2Changed = 0; +int gfxBG3Changed = 0; + +int gfxBG2X = 0; +int gfxBG2Y = 0; +int gfxBG2LastX = 0; +int gfxBG2LastY = 0; +int gfxBG3X = 0; +int gfxBG3Y = 0; +int gfxBG3LastX = 0; +int gfxBG3LastY = 0; +int gfxLastVCOUNT = 0; diff --git a/src/Gfx.h b/src/Gfx.h new file mode 100644 index 0000000..36c6592 --- /dev/null +++ b/src/Gfx.h @@ -0,0 +1,1809 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GFX_H +#define VBA_GFX_H + +#include "GBA.h" +#include "Gfx.h" +#include "Globals.h" + +#include "Port.h" + +//#define SPRITE_DEBUG + +void gfxDrawTextScreen(u16, u16, u16, u32 *); +void gfxDrawRotScreen(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +void gfxDrawRotScreen16Bit(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +void gfxDrawRotScreen256(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +void gfxDrawRotScreen16Bit160(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +void gfxDrawSprites(u32 *); +void gfxIncreaseBrightness(u32 *line, int coeff); +void gfxDecreaseBrightness(u32 *line, int coeff); +void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb); + +void mode0RenderLine(); +void mode0RenderLineNoWindow(); +void mode0RenderLineAll(); + +void mode1RenderLine(); +void mode1RenderLineNoWindow(); +void mode1RenderLineAll(); + +void mode2RenderLine(); +void mode2RenderLineNoWindow(); +void mode2RenderLineAll(); + +void mode3RenderLine(); +void mode3RenderLineNoWindow(); +void mode3RenderLineAll(); + +void mode4RenderLine(); +void mode4RenderLineNoWindow(); +void mode4RenderLineAll(); + +void mode5RenderLine(); +void mode5RenderLineNoWindow(); +void mode5RenderLineAll(); + +extern int coeff[32]; +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; +extern u32 lineOBJ[240]; +extern u32 lineOBJWin[240]; +extern u32 lineMix[240]; +extern bool gfxInWin0[240]; +extern bool gfxInWin1[240]; + +extern int gfxBG2Changed; +extern int gfxBG3Changed; + +extern int gfxBG2X; +extern int gfxBG2Y; +extern int gfxBG2LastX; +extern int gfxBG2LastY; +extern int gfxBG3X; +extern int gfxBG3Y; +extern int gfxBG3LastX; +extern int gfxBG3LastY; +extern int gfxLastVCOUNT; + +inline void gfxClearArray(u32 *array) +{ + for(int i = 0; i < 240; i++) + { + *array++ = 0x80000000; + } +} + +inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch((control >> 14) & 3) + { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + VCOUNT) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if(mosaicOn) + { + if((VCOUNT % mosaicY) != 0) + { + mosaicY = (VCOUNT / mosaicY) * mosaicY; + yyy = (vofs + mosaicY) & maskY; + } + } + + if(yyy > 255 && sizeY > 256) + { + yyy &= 255; + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + if((control) & 0x80) + { + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + for(int x = 0; x < 240; x++) + { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + + line[x] = color ? (READ16LE(&palette[color]) | prio): 0x80000000; + + if(data & 0x0400) + { + if(tileX == 0) + screenSource++; + } + else if(tileX == 7) + screenSource++; + xxx++; + if(xxx == 256) + { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else + { + screenSource = screenBase + yshift; + xxx = 0; + } + } + else if(xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + else + { + u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) + + yshift; + for(int x = 0; x < 240; x++) + { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)]; + + if(tileX & 1) + { + color = (color >> 4); + } + else + { + color &= 0x0F; + } + + int pal = (READ16LE(screenSource)>>8) & 0xF0; + line[x] = color ? (READ16LE(&palette[pal + color])|prio): 0x80000000; + + if(data & 0x0400) + { + if(tileX == 0) + screenSource++; + } + else if(tileX == 7) + screenSource++; + xxx++; + if(xxx == 256) + { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else + { + screenSource = screenBase + yshift; + xxx = 0; + } + } + else if(xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + if(mosaicOn) + { + if(mosaicX > 1) + { + int m = 1; + for(int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if(m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) * 0x800]; + int prio = ((control & 3) << 25) + 0x1000000; + + int sizeX = 128; + int sizeY = 128; + switch((control >> 14) & 3) + { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } + else + { + currentX += dmx; + } + + if(changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + if(control & 0x2000) + { + xxx %= sizeX; + yyy %= sizeY; + if(xxx < 0) + xxx += sizeX; + if(yyy < 0) + yyy += sizeY; + } + + if(control & 0x80) + { + for(int x = 0; x < 240; x++) + { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + int tile = screenBase[(xxx>>3) + (yyy>>3)*(sizeX>>3)]; + + int tileX = (xxx & 7); + int tileY = yyy & 7; + + u8 color = charBase[(tile<<6) + (tileY<<3) + tileX]; + + line[x] = color ? (READ16LE(&palette[color])|prio): 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + + if(control & 0x2000) + { + xxx %= sizeX; + yyy %= sizeY; + if(xxx < 0) + xxx += sizeX; + if(yyy < 0) + yyy += sizeY; + } + } + } + else + { + for(int x = 0; x < 240; x++) + { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + int tile = screenBase[(xxx>>3) + (yyy>>3)*(sizeX>>3)]; + + int tileX = (xxx & 7); + int tileY = yyy & 7; + + u8 color = charBase[(tile<<6) + (tileY<<3) + tileX]; + + line[x] = color ? (READ16LE(&palette[color])|prio): 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + + if(control & 0x2000) + { + xxx %= sizeX; + yyy %= sizeY; + if(xxx < 0) + xxx += sizeX; + if(yyy < 0) + yyy += sizeY; + } + } + } + + if(control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) + { + int m = 1; + for(int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if(m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen16Bit(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } + else + currentX += dmx; + + if(changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) + { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) + { + int m = 1; + for(int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if(m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen256(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int ¤tX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *screenBase = (DISPCNT & 0x0010) ? &vram[0xA000] : &vram[0x0000]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } + else + { + currentX += dmx; + } + + if(changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT / mosaicY) * mosaicY; + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) + { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + u8 color = screenBase[yyy * 240 + xxx]; + + line[x] = color ? (READ16LE(&palette[color])|prio): 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) + { + int m = 1; + for(int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if(m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen16Bit160(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (DISPCNT & 0x0010) ? (u16 *)&vram[0xa000] : + (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 160; + int sizeY = 128; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } + else + { + currentX += dmx; + } + + if(changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT / mosaicY) * mosaicY; + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) + { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) + { + int m = 1; + for(int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if(m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawSprites(u32 *lineOBJ) +{ + int m=0; + gfxClearArray(lineOBJ); + if(layerEnable & 0x1000) + { + u16 *sprites = (u16 *)oam; + u16 *spritePalette = &((u16 *)paletteRAM)[256]; + int mosaicY = ((MOSAIC & 0xF000)>>12) + 1; + int mosaicX = ((MOSAIC & 0xF00)>>8) + 1; + for(int x = 0; x < 128 ; x++) + { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + // ignore OBJ-WIN + if((a0 & 0x0c00) == 0x0800) + continue; + + int sizeY = 8; + int sizeX = 8; + + switch(((a0 >>12) & 0x0c)|(a1>>14)) + { + case 0: + break; + case 1: + sizeX = sizeY = 16; + break; + case 2: + sizeX = sizeY = 32; + break; + case 3: + sizeX = sizeY = 64; + break; + case 4: + sizeX = 16; + break; + case 5: + sizeX = 32; + break; + case 6: + sizeX = 32; + sizeY = 16; + break; + case 7: + sizeX = 64; + sizeY = 32; + break; + case 8: + sizeY = 16; + break; + case 9: + sizeY = 32; + break; + case 10: + sizeX = 16; + sizeY = 32; + break; + case 11: + sizeX = 32; + sizeY = 64; + break; + default: + continue; + } + +#ifdef SPRITE_DEBUG + int maskX = sizeX-1; + int maskY = sizeY-1; +#endif + + int sy = (a0 & 255); + + if(sy > 160) + sy -= 256; + + if(a0 & 0x0100) + { + int fieldX = sizeX; + int fieldY = sizeY; + if(a0 & 0x0200) + { + fieldX <<= 1; + fieldY <<= 1; + } + + int t = VCOUNT - sy; + if((t >= 0) && (t < fieldY)) + { + int sx = (a1 & 0x1FF); + if((sx < 240) || (((sx + fieldX) & 511) < 240)) + { + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + if(a0 & 0x1000) + { + t -= (t % mosaicY); + } + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if(a0 & 0x2000) + { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240); + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7FFF)]; + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) + { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + sx = (sx+1)&511; + ; + realX += dx; + realY += dy; + } + } + else + { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for(int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240); + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7FFF)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[palette+color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if((a0 & 0x1000) && m) + { + m++; + if (m==mosaicX) + m=0; + } + +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1)&511; + ; + realX += dx; + realY += dy; + + } + } + } + } + } + else + { + int t = VCOUNT - sy; + if((t >= 0) && (t < sizeY)) + { + int sx = (a1 & 0x1FF); + if(((sx < 240)||(((sx+sizeX)&511)<240)) && !(a0 & 0x0200)) + { + if(a0 & 0x2000) + { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + { + inc = sizeX >> 2; + } + else + { + c &= 0x3FE; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + + if(a0 & 0x1000) + { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF); + + if(a1 & 0x1000) + xxx = 7; + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + for(int xx = 0; xx < sizeX; xx++) + { + if(sx < 240) + { + u8 color = vram[address]; + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) + { + m++; + if (m==mosaicX) + m=0; + } + +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) + { + xxx--; + address--; + if(xxx == -1) + { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } + else + { + xxx++; + address++; + if(xxx == 8) + { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + { + inc = sizeX >> 3; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + + if(a0 & 0x1000) + { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF); + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) + { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) + { + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if (a0 & 0x1000) + { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) + { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } + else + { + for(int xx = 0; xx < sizeX; xx++) + { + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + + } + } + if (a0 & 0x1000) + { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) + { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +inline void gfxDrawOBJWin(u32 *lineOBJWin) +{ + gfxClearArray(lineOBJWin); + if(layerEnable & 0x8000) + { + u16 *sprites = (u16 *)oam; + // u16 *spritePalette = &((u16 *)paletteRAM)[256]; + for(int x = 0; x < 128 ; x++) + { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + // ignore non OBJ-WIN + if((a0 & 0x0c00) != 0x0800) + continue; + + int sizeY = 8; + int sizeX = 8; + + switch(((a0 >>12) & 0x0c)|(a1>>14)) + { + case 0: + break; + case 1: + sizeX = sizeY = 16; + break; + case 2: + sizeX = sizeY = 32; + break; + case 3: + sizeX = sizeY = 64; + break; + case 4: + sizeX = 16; + break; + case 5: + sizeX = 32; + break; + case 6: + sizeX = 32; + sizeY = 16; + break; + case 7: + sizeX = 64; + sizeY = 32; + break; + case 8: + sizeY = 16; + break; + case 9: + sizeY = 32; + break; + case 10: + sizeX = 16; + sizeY = 32; + break; + case 11: + sizeX = 32; + sizeY = 64; + break; + default: + continue; + } + + int sy = (a0 & 255); + + if(sy > 160) + sy -= 256; + + if(a0 & 0x0100) + { + int fieldX = sizeX; + int fieldY = sizeY; + if(a0 & 0x0200) + { + fieldX <<= 1; + fieldY <<= 1; + } + + int t = VCOUNT - sy; + if((t >= 0) && (t < fieldY)) + { + int sx = (a1 & 0x1FF); + if((sx < 240) || (((sx + fieldX) & 511) < 240)) + { + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if(a0 & 0x2000) + { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY) + {} + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7fff)]; + if(color) + { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1)&511; + ; + realX += dx; + realY += dy; + } + } + else + { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + // int palette = (a2 >> 8) & 0xF0; + for(int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + + // if(x == 0 || x == (sizeX-1) || + // t == 0 || t == (sizeY-1)) { + // lineOBJ[sx] = 0x001F | prio; + // } else { + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY) + {} + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7fff)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if(color) + { + lineOBJWin[sx] = 1; + } + } + // } + sx = (sx+1)&511; + ; + realX += dx; + realY += dy; + } + } + } + } + } + else + { + int t = VCOUNT - sy; + if((t >= 0) && (t < sizeY)) + { + int sx = (a1 & 0x1FF); + if(((sx < 240)||(((sx+sizeX)&511)<240)) && !(a0 & 0x0200)) + { + if(a0 & 0x2000) + { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + { + inc = sizeX >> 2; + } + else + { + c &= 0x3FE; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff); + if(a1 & 0x1000) + xxx = 7; + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + for(int xx = 0; xx < sizeX; xx++) + { + if(sx < 240) + { + u8 color = vram[address]; + if(color) + { + lineOBJWin[sx] = 1; + } + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) + { + xxx--; + address--; + if(xxx == -1) + { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } + else + { + xxx++; + address++; + if(xxx == 8) + { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + { + inc = sizeX >> 3; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff); + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + // int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) + { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) + { + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if(color) + { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) + { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } + else + { + for(int xx = 0; xx < sizeX; xx++) + { + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if(color) + { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) + { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +inline u32 gfxIncreaseBrightness(u32 color, int coeff) +{ + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r + (((31 - r) * coeff) >> 4); + g = g + (((31 - g) * coeff) >> 4); + b = b + (((31 - b) * coeff) >> 4); + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + return color; +} + +inline void gfxIncreaseBrightness(u32 *line, int coeff) +{ + for(int x = 0; x < 240; x++) + { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r + (((31 - r) * coeff) >> 4); + g = g + (((31 - g) * coeff) >> 4); + b = b + (((31 - b) * coeff) >> 4); + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +inline u32 gfxDecreaseBrightness(u32 color, int coeff) +{ + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r - ((r * coeff) >> 4); + g = g - ((g * coeff) >> 4); + b = b - ((b * coeff) >> 4); + if(r < 0) + r = 0; + if(g < 0) + g = 0; + if(b < 0) + b = 0; + color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + + return color; +} + +inline void gfxDecreaseBrightness(u32 *line, int coeff) +{ + for(int x = 0; x < 240; x++) + { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r - ((r * coeff) >> 4); + g = g - ((g * coeff) >> 4); + b = b - ((b * coeff) >> 4); + if(r < 0) + r = 0; + if(g < 0) + g = 0; + if(b < 0) + b = 0; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +inline u32 gfxAlphaBlend(u32 color, u32 color2, int ca, int cb) +{ + if(color < 0x80000000) + { + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + int r0 = (color2 & 0x1F); + int g0 = ((color2 >> 5) & 0x1F); + int b0 = ((color2 >> 10) & 0x1F); + + r = ((r * ca) >> 4) + ((r0 * cb) >> 4); + g = ((g * ca) >> 4) + ((g0 * cb) >> 4); + b = ((b * ca) >> 4) + ((b0 * cb) >> 4); + + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + + return (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } + return color; +} + +inline void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb) +{ + for(int x = 0; x < 240; x++) + { + u32 color = *ta; + if(color < 0x80000000) + { + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + u32 color2 = (*tb++); + int r0 = (color2 & 0x1F); + int g0 = ((color2 >> 5) & 0x1F); + int b0 = ((color2 >> 10) & 0x1F); + + r = ((r * ca) >> 4) + ((r0 * cb) >> 4); + g = ((g * ca) >> 4) + ((g0 * cb) >> 4); + b = ((b * ca) >> 4) + ((b0 * cb) >> 4); + + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + + *ta++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } + else + { + ta++; + tb++; + } + } +} + +#endif // VBA_GFX_H diff --git a/src/Globals.cpp b/src/Globals.cpp new file mode 100644 index 0000000..f6e7968 --- /dev/null +++ b/src/Globals.cpp @@ -0,0 +1,135 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" + +reg_pair reg[45]; +memoryMap map[256]; +bool ioReadable[0x400]; +bool N_FLAG = 0; +bool C_FLAG = 0; +bool Z_FLAG = 0; +bool V_FLAG = 0; +bool armState = true; +bool armIrqEnable = true; +u32 armNextPC = 0x00000000; +int armMode = 0x1f; +u32 stop = 0x08000568; +int saveType = 0; +bool useBios = false; +bool skipBios = false; +int frameSkip = 1; +bool speedup = false; +bool synchronize = true; +bool cpuDisableSfx = false; +bool cpuIsMultiBoot = false; +bool parseDebug = true; +int layerSettings = 0xff00; +int layerEnable = 0xff00; +bool speedHack = false; +int cpuSaveType = 0; +bool cpuEnhancedDetection = true; +bool cheatsEnabled = true; + +u8 *bios = NULL; +u8 *rom = NULL; +u8 *internalRAM = NULL; +u8 *workRAM = NULL; +u8 *paletteRAM = NULL; +u8 *vram = NULL; +u8 *pix = NULL; +u8 *oam = NULL; +u8 *ioMem = NULL; + +u16 DISPCNT = 0x0080; +u16 DISPSTAT = 0x0000; +u16 VCOUNT = 0x0000; +u16 BG0CNT = 0x0000; +u16 BG1CNT = 0x0000; +u16 BG2CNT = 0x0000; +u16 BG3CNT = 0x0000; +u16 BG0HOFS = 0x0000; +u16 BG0VOFS = 0x0000; +u16 BG1HOFS = 0x0000; +u16 BG1VOFS = 0x0000; +u16 BG2HOFS = 0x0000; +u16 BG2VOFS = 0x0000; +u16 BG3HOFS = 0x0000; +u16 BG3VOFS = 0x0000; +u16 BG2PA = 0x0100; +u16 BG2PB = 0x0000; +u16 BG2PC = 0x0000; +u16 BG2PD = 0x0100; +u16 BG2X_L = 0x0000; +u16 BG2X_H = 0x0000; +u16 BG2Y_L = 0x0000; +u16 BG2Y_H = 0x0000; +u16 BG3PA = 0x0100; +u16 BG3PB = 0x0000; +u16 BG3PC = 0x0000; +u16 BG3PD = 0x0100; +u16 BG3X_L = 0x0000; +u16 BG3X_H = 0x0000; +u16 BG3Y_L = 0x0000; +u16 BG3Y_H = 0x0000; +u16 WIN0H = 0x0000; +u16 WIN1H = 0x0000; +u16 WIN0V = 0x0000; +u16 WIN1V = 0x0000; +u16 WININ = 0x0000; +u16 WINOUT = 0x0000; +u16 MOSAIC = 0x0000; +u16 BLDMOD = 0x0000; +u16 COLEV = 0x0000; +u16 COLY = 0x0000; +u16 DM0SAD_L = 0x0000; +u16 DM0SAD_H = 0x0000; +u16 DM0DAD_L = 0x0000; +u16 DM0DAD_H = 0x0000; +u16 DM0CNT_L = 0x0000; +u16 DM0CNT_H = 0x0000; +u16 DM1SAD_L = 0x0000; +u16 DM1SAD_H = 0x0000; +u16 DM1DAD_L = 0x0000; +u16 DM1DAD_H = 0x0000; +u16 DM1CNT_L = 0x0000; +u16 DM1CNT_H = 0x0000; +u16 DM2SAD_L = 0x0000; +u16 DM2SAD_H = 0x0000; +u16 DM2DAD_L = 0x0000; +u16 DM2DAD_H = 0x0000; +u16 DM2CNT_L = 0x0000; +u16 DM2CNT_H = 0x0000; +u16 DM3SAD_L = 0x0000; +u16 DM3SAD_H = 0x0000; +u16 DM3DAD_L = 0x0000; +u16 DM3DAD_H = 0x0000; +u16 DM3CNT_L = 0x0000; +u16 DM3CNT_H = 0x0000; +u16 TM0D = 0x0000; +u16 TM0CNT = 0x0000; +u16 TM1D = 0x0000; +u16 TM1CNT = 0x0000; +u16 TM2D = 0x0000; +u16 TM2CNT = 0x0000; +u16 TM3D = 0x0000; +u16 TM3CNT = 0x0000; +u16 P1 = 0xFFFF; +u16 IE = 0x0000; +u16 IF = 0x0000; +u16 IME = 0x0000; diff --git a/src/Globals.h b/src/Globals.h new file mode 100644 index 0000000..86c4aee --- /dev/null +++ b/src/Globals.h @@ -0,0 +1,149 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GLOBALS_H +#define VBA_GLOBALS_H + +#define VERBOSE_SWI 1 +#define VERBOSE_UNALIGNED_MEMORY 2 +#define VERBOSE_ILLEGAL_WRITE 4 +#define VERBOSE_ILLEGAL_READ 8 +#define VERBOSE_DMA0 16 +#define VERBOSE_DMA1 32 +#define VERBOSE_DMA2 64 +#define VERBOSE_DMA3 128 +#define VERBOSE_UNDEFINED 256 +#define VERBOSE_AGBPRINT 512 + +extern reg_pair reg[45]; +extern bool ioReadable[0x400]; +extern bool N_FLAG; +extern bool C_FLAG; +extern bool Z_FLAG; +extern bool V_FLAG; +extern bool armState; +extern bool armIrqEnable; +extern u32 armNextPC; +extern int armMode; +extern u32 stop; +extern int saveType; +extern bool useBios; +extern bool skipBios; +extern int frameSkip; +extern bool speedup; +extern bool synchronize; +extern bool cpuDisableSfx; +extern bool cpuIsMultiBoot; +extern bool parseDebug; +extern int layerSettings; +extern int layerEnable; +extern bool speedHack; +extern int cpuSaveType; +extern bool cpuEnhancedDetection; +extern bool cheatsEnabled; + +extern u8 *bios; +extern u8 *rom; +extern u8 *internalRAM; +extern u8 *workRAM; +extern u8 *paletteRAM; +extern u8 *vram; +extern u8 *pix; +extern u8 *oam; +extern u8 *ioMem; + +extern u16 DISPCNT; +extern u16 DISPSTAT; +extern u16 VCOUNT; +extern u16 BG0CNT; +extern u16 BG1CNT; +extern u16 BG2CNT; +extern u16 BG3CNT; +extern u16 BG0HOFS; +extern u16 BG0VOFS; +extern u16 BG1HOFS; +extern u16 BG1VOFS; +extern u16 BG2HOFS; +extern u16 BG2VOFS; +extern u16 BG3HOFS; +extern u16 BG3VOFS; +extern u16 BG2PA; +extern u16 BG2PB; +extern u16 BG2PC; +extern u16 BG2PD; +extern u16 BG2X_L; +extern u16 BG2X_H; +extern u16 BG2Y_L; +extern u16 BG2Y_H; +extern u16 BG3PA; +extern u16 BG3PB; +extern u16 BG3PC; +extern u16 BG3PD; +extern u16 BG3X_L; +extern u16 BG3X_H; +extern u16 BG3Y_L; +extern u16 BG3Y_H; +extern u16 WIN0H; +extern u16 WIN1H; +extern u16 WIN0V; +extern u16 WIN1V; +extern u16 WININ; +extern u16 WINOUT; +extern u16 MOSAIC; +extern u16 BLDMOD; +extern u16 COLEV; +extern u16 COLY; +extern u16 DM0SAD_L; +extern u16 DM0SAD_H; +extern u16 DM0DAD_L; +extern u16 DM0DAD_H; +extern u16 DM0CNT_L; +extern u16 DM0CNT_H; +extern u16 DM1SAD_L; +extern u16 DM1SAD_H; +extern u16 DM1DAD_L; +extern u16 DM1DAD_H; +extern u16 DM1CNT_L; +extern u16 DM1CNT_H; +extern u16 DM2SAD_L; +extern u16 DM2SAD_H; +extern u16 DM2DAD_L; +extern u16 DM2DAD_H; +extern u16 DM2CNT_L; +extern u16 DM2CNT_H; +extern u16 DM3SAD_L; +extern u16 DM3SAD_H; +extern u16 DM3DAD_L; +extern u16 DM3DAD_H; +extern u16 DM3CNT_L; +extern u16 DM3CNT_H; +extern u16 TM0D; +extern u16 TM0CNT; +extern u16 TM1D; +extern u16 TM1CNT; +extern u16 TM2D; +extern u16 TM2CNT; +extern u16 TM3D; +extern u16 TM3CNT; +extern u16 P1; +extern u16 IE; +extern u16 IF; +extern u16 IME; + +#endif // VBA_GLOBALS_H diff --git a/src/Mode0.cpp b/src/Mode0.cpp new file mode 100644 index 0000000..b2a0187 --- /dev/null +++ b/src/Mode0.cpp @@ -0,0 +1,652 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "Globals.h" +#include "Gfx.h" + +void mode0RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + return; + } + + if(layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) + { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if(layerEnable & 0x0800) + { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24)) + { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24)) + { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } +} + +void mode0RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + return; + } + + if(layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) + { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if(layerEnable & 0x0800) + { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + int effect = (BLDMOD >> 6) & 3; + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if(line1[x] < (color & 0xFF000000)) + { + color = line1[x]; + top = 0x02; + } + + if(line2[x] < (color & 0xFF000000)) + { + color = line2[x]; + top = 0x04; + } + + if(line3[x] < (color & 0xFF000000)) + { + color = line3[x]; + top = 0x08; + } + + if(lineOBJ[x] < (color & 0xFF000000)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) + { + switch(effect) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if(line0[x] < back) + { + if(top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if(line1[x] < (back & 0xFF000000)) + { + if(top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if(line2[x] < (back & 0xFF000000)) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if(line3[x] < (back & 0xFF000000)) + { + if(top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if(lineOBJ[x] < (back & 0xFF000000)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if(line0[x] < back) + { + back = line0[x]; + top2 = 0x01; + } + + if(line1[x] < (back & 0xFF000000)) + { + back = line1[x]; + top2 = 0x02; + } + + if(line2[x] < (back & 0xFF000000)) + { + back = line2[x]; + top2 = 0x04; + } + + if(line3[x] < (back & 0xFF000000)) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } +} + +void mode0RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if((layerEnable & 0x0100)) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if((layerEnable & 0x0200)) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if((layerEnable & 0x0400)) + { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if((layerEnable & 0x0800)) + { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if(inWindow1) + { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) + { + if(gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if((mask & 1) && (line0[x] < color)) + { + color = line0[x]; + top = 0x01; + } + + if((mask & 2) && ((u8)(line1[x]>>24) < (u8)(color >> 24))) + { + color = line1[x]; + top = 0x02; + } + + if((mask & 4) && ((u8)(line2[x]>>24) < (u8)(color >> 24))) + { + color = line2[x]; + top = 0x04; + } + + if((mask & 8) && ((u8)(line3[x]>>24) < (u8)(color >> 24))) + { + color = line3[x]; + top = 0x08; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + // special FX on in the window + if(mask & 32) + { + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 1) && ((u8)(line0[x]>>24) < (u8)(back >> 24))) + { + back = line0[x]; + top2 = 0x01; + } + + if((mask & 2) && ((u8)(line1[x]>>24) < (u8)(back >> 24))) + { + back = line1[x]; + top2 = 0x02; + } + + if((mask & 4) && ((u8)(line2[x]>>24) < (u8)(back >> 24))) + { + back = line2[x]; + top2 = 0x04; + } + + if((mask & 8) && ((u8)(line3[x]>>24) < (u8)(back >> 24))) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if(color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 1) && ((u8)(line0[x]>>24) < (u8)(back >> 24))) + { + back = line0[x]; + top2 = 0x01; + } + + if((mask & 2) && ((u8)(line1[x]>>24) < (u8)(back >> 24))) + { + back = line1[x]; + top2 = 0x02; + } + + if((mask & 4) && ((u8)(line2[x]>>24) < (u8)(back >> 24))) + { + back = line2[x]; + top2 = 0x04; + } + + if((mask & 8) && ((u8)(line3[x]>>24) < (u8)(back >> 24))) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } +} diff --git a/src/Mode1.cpp b/src/Mode1.cpp new file mode 100644 index 0000000..f1ccdbf --- /dev/null +++ b/src/Mode1.cpp @@ -0,0 +1,599 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "Globals.h" +#include "Gfx.h" + +void mode1RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24)) + { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode1RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + + if(layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if(line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24)) + { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode1RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if(layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if(inWindow1) + { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) + { + if(gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if(line0[x] < color && (mask & 1)) + { + color = line0[x]; + top = 0x01; + } + + if((u8)(line1[x]>>24) < (u8)(color >> 24) && (mask & 2)) + { + color = line1[x]; + top = 0x02; + } + + if((u8)(line2[x]>>24) < (u8)(color >> 24) && (mask & 4)) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24) && (mask & 16)) + { + color = lineOBJ[x]; + top = 0x10; + } + + // special FX on the window + if(mask & 32) + { + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if(color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/Mode2.cpp b/src/Mode2.cpp new file mode 100644 index 0000000..e2954cb --- /dev/null +++ b/src/Mode2.cpp @@ -0,0 +1,550 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "Globals.h" +#include "Gfx.h" + +void mode2RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if(layerEnable & 0x0800) + { + int changed = gfxBG3Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24)) + { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode2RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if(layerEnable & 0x0800) + { + int changed = gfxBG3Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + + if((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24)) + { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode2RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if(layerEnable & 0x0800) + { + int changed = gfxBG3Changed; + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if(inWindow1) + { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) + { + if(gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if(line2[x] < color && (mask & 4)) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(line3[x]>>24) < (u8)(color >> 24) && (mask & 8)) + { + color = line3[x]; + top = 0x08; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24) && (mask & 16)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(mask & 32) + { + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if(color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/Mode3.cpp b/src/Mode3.cpp new file mode 100644 index 0000000..8a61021 --- /dev/null +++ b/src/Mode3.cpp @@ -0,0 +1,463 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "Globals.h" +#include "Gfx.h" + +void mode3RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode3RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode3RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x80) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if(inWindow1) + { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) + { + if(gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if((mask & 4) && (line2[x] < color)) + { + color = line2[x]; + top = 0x04; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(mask & 32) + { + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if(color & 0x00010000) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/Mode4.cpp b/src/Mode4.cpp new file mode 100644 index 0000000..a451172 --- /dev/null +++ b/src/Mode4.cpp @@ -0,0 +1,460 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "Gfx.h" +#include "Globals.h" + +void mode4RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x0080) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if(line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if(line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode4RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x0080) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if(layerEnable & 0x400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if(line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if(line2[x] < back) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if(line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode4RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if(DISPCNT & 0x0080) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if(layerEnable & 0x400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for(int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if(inWindow1) + { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) + { + if(gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if((mask & 4) && (line2[x] < color)) + { + color = line2[x]; + top = 0x04; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(mask & 32) + { + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if(color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/Mode5.cpp b/src/Mode5.cpp new file mode 100644 index 0000000..83c4390 --- /dev/null +++ b/src/Mode5.cpp @@ -0,0 +1,463 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "Globals.h" +#include "Gfx.h" + +void mode5RenderLine() +{ + if(DISPCNT & 0x0080) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode5RenderLineNoWindow() +{ + if(DISPCNT & 0x0080) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = ( READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if(line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if(line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode5RenderLineAll() +{ + if(DISPCNT & 0x0080) + { + for(int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if(layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if(gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + bool inWindow0 = false; + bool inWindow1 = false; + + if(layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if(layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + u8 mask = outMask; + + if(!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if(inWindow1) + { + if(gfxInWin1[x]) + mask = inWin1Mask; + } + + if(inWindow0) + { + if(gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if((mask & 4) && (line2[x] < color)) + { + color = line2[x]; + top = 0x04; + } + + if((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + if(mask & 32) + { + if(!(color & 0x00010000)) + { + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if(top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + if(top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if(top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if(color & 0x00010000) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if(top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch((BLDMOD >> 6) & 3) + { + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} diff --git a/src/NLS.h b/src/NLS.h new file mode 100644 index 0000000..ca024de --- /dev/null +++ b/src/NLS.h @@ -0,0 +1,62 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#define N_(String) (String) + +#define MSG_UNSUPPORTED_VBA_SGM 1 +#define MSG_CANNOT_LOAD_SGM 2 +#define MSG_SAVE_GAME_NOT_USING_BIOS 3 +#define MSG_SAVE_GAME_USING_BIOS 4 +#define MSG_UNSUPPORTED_SAVE_TYPE 5 +#define MSG_CANNOT_OPEN_FILE 6 +#define MSG_BAD_ZIP_FILE 7 +#define MSG_NO_IMAGE_ON_ZIP 8 +#define MSG_ERROR_OPENING_IMAGE 9 +#define MSG_ERROR_READING_IMAGE 10 +#define MSG_UNSUPPORTED_BIOS_FUNCTION 11 +#define MSG_INVALID_BIOS_FILE_SIZE 12 +#define MSG_INVALID_CHEAT_CODE 13 +#define MSG_UNKNOWN_ARM_OPCODE 14 +#define MSG_UNKNOWN_THUMB_OPCODE 15 +#define MSG_ERROR_CREATING_FILE 16 +#define MSG_FAILED_TO_READ_SGM 17 +#define MSG_FAILED_TO_READ_RTC 18 +#define MSG_UNSUPPORTED_VB_SGM 19 +#define MSG_CANNOT_LOAD_SGM_FOR 20 +#define MSG_ERROR_OPENING_IMAGE_FROM 21 +#define MSG_ERROR_READING_IMAGE_FROM 22 +#define MSG_UNSUPPORTED_ROM_SIZE 23 +#define MSG_UNSUPPORTED_RAM_SIZE 24 +#define MSG_UNKNOWN_CARTRIDGE_TYPE 25 +#define MSG_MAXIMUM_NUMBER_OF_CHEATS 26 +#define MSG_INVALID_GAMESHARK_CODE 27 +#define MSG_INVALID_GAMEGENIE_CODE 28 +#define MSG_INVALID_CHEAT_TO_REMOVE 29 +#define MSG_INVALID_CHEAT_CODE_ADDRESS 30 +#define MSG_UNSUPPORTED_CHEAT_LIST_VERSION 31 +#define MSG_UNSUPPORTED_CHEAT_LIST_TYPE 32 +#define MSG_INVALID_GSA_CODE 33 +#define MSG_CANNOT_IMPORT_SNAPSHOT_FOR 34 +#define MSG_UNSUPPORTED_SNAPSHOT_FILE 35 +#define MSG_UNSUPPORTED_ARM_MODE 36 +#define MSG_UNSUPPORTED_CODE_FILE 37 +#define MSG_GBA_CODE_WARNING 38 +#define MSG_INVALID_CBA_CODE 39 +#define MSG_CBA_CODE_WARNING 40 +#define MSG_OUT_OF_MEMORY 41 diff --git a/src/Port.h b/src/Port.h new file mode 100644 index 0000000..b5c2cc4 --- /dev/null +++ b/src/Port.h @@ -0,0 +1,77 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_PORT_H +#define VBA_PORT_H + +// swaps a 16-bit value +static inline u16 swap16(u16 v) +{ + return (v<<8)|(v>>8); +} + +// swaps a 32-bit value +static inline u32 swap32(u32 v) +{ + return (v<<24)|((v<<8)&0xff0000)|((v>>8)&0xff00)|(v>>24); +} + +#ifdef WORDS_BIGENDIAN +#define __ppc__ + +#if defined(__GNUC__) && defined(__ppc__) + +#define READ16LE(base) \ + ({ unsigned short lhbrxResult; \ + __asm__ ("lhbrx %0, 0, %1" : "=r" (lhbrxResult) : "r" (base) : "memory"); \ + lhbrxResult; }) + +#define READ32LE(base) \ + ({ unsigned long lwbrxResult; \ + __asm__ ("lwbrx %0, 0, %1" : "=r" (lwbrxResult) : "r" (base) : "memory"); \ + lwbrxResult; }) + +#define WRITE16LE(base, value) \ + __asm__ ("sthbrx %0, 0, %1" : : "r" (value), "r" (base) : "memory") + +#define WRITE32LE(base, value) \ + __asm__ ("stwbrx %0, 0, %1" : : "r" (value), "r" (base) : "memory") + +#else +#define READ16LE(x) \ + swap16(*((u16 *)(x))) +#define READ32LE(x) \ + swap32(*((u32 *)(x))) +#define WRITE16LE(x,v) \ + *((u16 *)x) = swap16((v)) +#define WRITE32LE(x,v) \ + *((u32 *)x) = swap32((v)) +#endif +#else +#define READ16LE(x) \ + *((u16 *)x) +#define READ32LE(x) \ + *((u32 *)x) +#define WRITE16LE(x,v) \ + *((u16 *)x) = (v) +#define WRITE32LE(x,v) \ + *((u32 *)x) = (v) +#endif + +#endif diff --git a/src/RTC.cpp b/src/RTC.cpp new file mode 100644 index 0000000..6b76558 --- /dev/null +++ b/src/RTC.cpp @@ -0,0 +1,245 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "System.h" +#include "GBA.h" +#include "Globals.h" +#include "Port.h" +#include "Util.h" +#include "NLS.h" +#include "vmmem.h" + +#include +#include + +enum RTCSTATE { IDLE, COMMAND, DATA, READDATA }; + +typedef struct + { + u8 byte0; + u8 byte1; + u8 byte2; + u8 command; + int dataLen; + int bits; + RTCSTATE state; + u8 data[12]; + // reserved variables for future + u8 reserved[12]; + bool reserved2; + u32 reserved3; + } +RTCCLOCKDATA; + +static RTCCLOCKDATA rtcClockData; +static bool rtcEnabled = false; + +void rtcEnable(bool e) +{ + rtcEnabled = e; +} + +bool rtcIsEnabled() +{ + return rtcEnabled; +} + +u16 rtcRead(u32 address) +{ + if(rtcEnabled) + { + if(address == 0x80000c8) + return rtcClockData.byte2; + else if(address == 0x80000c6) + return rtcClockData.byte1; + else if(address == 0x80000c4) + { + return rtcClockData.byte0; + } + } + + //return READ16LE((&rom[address & 0x1FFFFFE])); + return VMRead16( address & 0x1FFFFFE ); +} + +static u8 toBCD(u8 value) +{ + value = value % 100; + int l = value % 10; + int h = value / 10; + return h * 16 + l; +} + +bool rtcWrite(u32 address, u16 value) +{ + if(!rtcEnabled) + return false; + + if(address == 0x80000c8) + { + rtcClockData.byte2 = (u8)value; // enable ? + } + else if(address == 0x80000c6) + { + rtcClockData.byte1 = (u8)value; // read/write + } + else if(address == 0x80000c4) + { + if(rtcClockData.byte2 & 1) + { + if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) + { + rtcClockData.state = COMMAND; + rtcClockData.bits = 0; + rtcClockData.command = 0; + } + else if(!(rtcClockData.byte0 & 1) && (value & 1)) + { // bit transfer + rtcClockData.byte0 = (u8)value; + switch(rtcClockData.state) + { + case COMMAND: + rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits); + rtcClockData.bits++; + if(rtcClockData.bits == 8) + { + rtcClockData.bits = 0; + switch(rtcClockData.command) + { + case 0x60: + // not sure what this command does but it doesn't take parameters + // maybe it is a reset or stop + rtcClockData.state = IDLE; + rtcClockData.bits = 0; + break; + case 0x62: + // this sets the control state but not sure what those values are + rtcClockData.state = READDATA; + rtcClockData.dataLen = 1; + break; + case 0x63: + rtcClockData.dataLen = 1; + rtcClockData.data[0] = 0x40; + rtcClockData.state = DATA; + break; + case 0x65: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 7; + rtcClockData.data[0] = toBCD(newtime->tm_year); + rtcClockData.data[1] = toBCD(newtime->tm_mon+1); + rtcClockData.data[2] = toBCD(newtime->tm_mday); + rtcClockData.data[3] = 0; + rtcClockData.data[4] = toBCD(newtime->tm_hour); + rtcClockData.data[5] = toBCD(newtime->tm_min); + rtcClockData.data[6] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + case 0x67: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 3; + rtcClockData.data[0] = toBCD(newtime->tm_hour); + rtcClockData.data[1] = toBCD(newtime->tm_min); + rtcClockData.data[2] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + default: + systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command); + rtcClockData.state = IDLE; + break; + } + } + break; + case DATA: + if(rtcClockData.byte1 & 2) + {} + else + { + rtcClockData.byte0 = (rtcClockData.byte0 & ~2) | + ((rtcClockData.data[rtcClockData.bits >> 3] >> + (rtcClockData.bits & 7)) & 1)*2; + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) + { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + case READDATA: + if(!(rtcClockData.byte1 & 2)) + {} + else + { + rtcClockData.data[rtcClockData.bits >> 3] = + (rtcClockData.data[rtcClockData.bits >> 3] >> 1) | + ((value << 6) & 128); + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) + { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + default: + break; + } + } + else + rtcClockData.byte0 = (u8)value; + } + } + return true; +} + +void rtcReset() +{ + memset(&rtcClockData, 0, sizeof(rtcClockData)); + + rtcClockData.byte0 = 0; + rtcClockData.byte1 = 0; + rtcClockData.byte2 = 0; + rtcClockData.command = 0; + rtcClockData.dataLen = 0; + rtcClockData.bits = 0; + rtcClockData.state = IDLE; +} + +void rtcSaveGame(gzFile gzFile) +{ + utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData)); +} + +void rtcReadGame(gzFile gzFile) +{ + utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData)); +} diff --git a/src/RTC.h b/src/RTC.h new file mode 100644 index 0000000..acbd3f6 --- /dev/null +++ b/src/RTC.h @@ -0,0 +1,31 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_RTC_H +#define VBA_RTC_H +extern u16 rtcRead(u32 address); +extern bool rtcWrite(u32 address, u16 value); +extern void rtcEnable(bool); +extern bool rtcIsEnabled(); +extern void rtcReset(); + +extern void rtcReadGame(gzFile gzFile); +extern void rtcSaveGame(gzFile gzFile); + +#endif diff --git a/src/Sound.cpp b/src/Sound.cpp new file mode 100644 index 0000000..4b49f16 --- /dev/null +++ b/src/Sound.cpp @@ -0,0 +1,1463 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "GBA.h" +#include "Globals.h" +#include "Sound.h" +#include "Util.h" + +#define USE_TICKS_AS 380 +#define SOUND_MAGIC 0x60000000 +#define SOUND_MAGIC_2 0x30000000 +#define NOISE_MAGIC 5 + +extern bool stopState; + +u8 soundWavePattern[4][32] = { + {0x01,0x01,0x01,0x01, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff}, + {0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff}, + {0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff}, + {0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff} + }; + +int soundFreqRatio[8] = { + 1048576, // 0 + 524288, // 1 + 262144, // 2 + 174763, // 3 + 131072, // 4 + 104858, // 5 + 87381, // 6 + 74898 // 7 + }; + +int soundShiftClock[16]= { + 2, // 0 + 4, // 1 + 8, // 2 + 16, // 3 + 32, // 4 + 64, // 5 + 128, // 6 + 256, // 7 + 512, // 8 + 1024, // 9 + 2048, // 10 + 4096, // 11 + 8192, // 12 + 16384, // 13 + 1, // 14 + 1 // 15 + }; + +int soundVolume = 0; + +u8 soundBuffer[6][735]; +u16 soundFinalWave[1470]; + +int soundBufferLen = 1470; +int soundBufferTotalLen = 14700; +int soundQuality = 2; +int soundPaused = 1; +int soundPlay = 0; +int soundTicks = soundQuality * USE_TICKS_AS; +int SOUND_CLOCK_TICKS = soundQuality * USE_TICKS_AS; +u32 soundNextPosition = 0; + +int soundLevel1 = 0; +int soundLevel2 = 0; +int soundBalance = 0; +int soundMasterOn = 0; +int soundIndex = 0; +int soundBufferIndex = 0; +int soundDebug = 0; +bool soundOffFlag = false; + +int sound1On = 0; +int sound1ATL = 0; +int sound1Skip = 0; +int sound1Index = 0; +int sound1Continue = 0; +int sound1EnvelopeVolume = 0; +int sound1EnvelopeATL = 0; +int sound1EnvelopeUpDown = 0; +int sound1EnvelopeATLReload = 0; +int sound1SweepATL = 0; +int sound1SweepATLReload = 0; +int sound1SweepSteps = 0; +int sound1SweepUpDown = 0; +int sound1SweepStep = 0; +u8 *sound1Wave = soundWavePattern[2]; + +int sound2On = 0; +int sound2ATL = 0; +int sound2Skip = 0; +int sound2Index = 0; +int sound2Continue = 0; +int sound2EnvelopeVolume = 0; +int sound2EnvelopeATL = 0; +int sound2EnvelopeUpDown = 0; +int sound2EnvelopeATLReload = 0; +u8 *sound2Wave = soundWavePattern[2]; + +int sound3On = 0; +int sound3ATL = 0; +int sound3Skip = 0; +int sound3Index = 0; +int sound3Continue = 0; +int sound3OutputLevel = 0; +int sound3Last = 0; +u8 sound3WaveRam[0x20]; +int sound3Bank = 0; +int sound3DataSize = 0; +int sound3ForcedOutput = 0; + +int sound4On = 0; +int sound4Clock = 0; +int sound4ATL = 0; +int sound4Skip = 0; +int sound4Index = 0; +int sound4ShiftRight = 0x7f; +int sound4ShiftSkip = 0; +int sound4ShiftIndex = 0; +int sound4NSteps = 0; +int sound4CountDown = 0; +int sound4Continue = 0; +int sound4EnvelopeVolume = 0; +int sound4EnvelopeATL = 0; +int sound4EnvelopeUpDown = 0; +int sound4EnvelopeATLReload = 0; + +int soundControl = 0; + +int soundDSFifoAIndex = 0; +int soundDSFifoACount = 0; +int soundDSFifoAWriteIndex = 0; +bool soundDSAEnabled = false; +int soundDSATimer = 0; +u8 soundDSFifoA[32]; +u8 soundDSAValue = 0; + +int soundDSFifoBIndex = 0; +int soundDSFifoBCount = 0; +int soundDSFifoBWriteIndex = 0; +bool soundDSBEnabled = false; +int soundDSBTimer = 0; +u8 soundDSFifoB[32]; +u8 soundDSBValue = 0; + +int soundEnableFlag = 0x3ff; + +s16 soundFilter[4000]; +s16 soundRight[5] = { 0, 0, 0, 0, 0 }; +s16 soundLeft[5] = { 0, 0, 0, 0, 0 }; +int soundEchoIndex = 0; +bool soundEcho = false; +bool soundLowPass = false; +bool soundReverse = false; + +variable_desc soundSaveStruct[] = { + { &soundPaused, sizeof(int) }, + { &soundPlay, sizeof(int) }, + { &soundTicks, sizeof(int) }, + { &SOUND_CLOCK_TICKS, sizeof(int) }, + { &soundLevel1, sizeof(int) }, + { &soundLevel2, sizeof(int) }, + { &soundBalance, sizeof(int) }, + { &soundMasterOn, sizeof(int) }, + { &soundIndex, sizeof(int) }, + { &sound1On, sizeof(int) }, + { &sound1ATL, sizeof(int) }, + { &sound1Skip, sizeof(int) }, + { &sound1Index, sizeof(int) }, + { &sound1Continue, sizeof(int) }, + { &sound1EnvelopeVolume, sizeof(int) }, + { &sound1EnvelopeATL, sizeof(int) }, + { &sound1EnvelopeATLReload, sizeof(int) }, + { &sound1EnvelopeUpDown, sizeof(int) }, + { &sound1SweepATL, sizeof(int) }, + { &sound1SweepATLReload, sizeof(int) }, + { &sound1SweepSteps, sizeof(int) }, + { &sound1SweepUpDown, sizeof(int) }, + { &sound1SweepStep, sizeof(int) }, + { &sound2On, sizeof(int) }, + { &sound2ATL, sizeof(int) }, + { &sound2Skip, sizeof(int) }, + { &sound2Index, sizeof(int) }, + { &sound2Continue, sizeof(int) }, + { &sound2EnvelopeVolume, sizeof(int) }, + { &sound2EnvelopeATL, sizeof(int) }, + { &sound2EnvelopeATLReload, sizeof(int) }, + { &sound2EnvelopeUpDown, sizeof(int) }, + { &sound3On, sizeof(int) }, + { &sound3ATL, sizeof(int) }, + { &sound3Skip, sizeof(int) }, + { &sound3Index, sizeof(int) }, + { &sound3Continue, sizeof(int) }, + { &sound3OutputLevel, sizeof(int) }, + { &sound4On, sizeof(int) }, + { &sound4ATL, sizeof(int) }, + { &sound4Skip, sizeof(int) }, + { &sound4Index, sizeof(int) }, + { &sound4Clock, sizeof(int) }, + { &sound4ShiftRight, sizeof(int) }, + { &sound4ShiftSkip, sizeof(int) }, + { &sound4ShiftIndex, sizeof(int) }, + { &sound4NSteps, sizeof(int) }, + { &sound4CountDown, sizeof(int) }, + { &sound4Continue, sizeof(int) }, + { &sound4EnvelopeVolume, sizeof(int) }, + { &sound4EnvelopeATL, sizeof(int) }, + { &sound4EnvelopeATLReload, sizeof(int) }, + { &sound4EnvelopeUpDown, sizeof(int) }, + { &soundEnableFlag, sizeof(int) }, + { &soundControl, sizeof(int) }, + { &soundDSFifoAIndex, sizeof(int) }, + { &soundDSFifoACount, sizeof(int) }, + { &soundDSFifoAWriteIndex, sizeof(int) }, + { &soundDSAEnabled, sizeof(bool) }, + { &soundDSATimer, sizeof(int) }, + { &soundDSFifoA[0], 32 }, + { &soundDSAValue, sizeof(u8) }, + { &soundDSFifoBIndex, sizeof(int) }, + { &soundDSFifoBCount, sizeof(int) }, + { &soundDSFifoBWriteIndex, sizeof(int) }, + { &soundDSBEnabled, sizeof(int) }, + { &soundDSBTimer, sizeof(int) }, + { &soundDSFifoB[0], 32 }, + { &soundDSBValue, sizeof(int) }, + { &soundBuffer[0][0], 6*735 }, + { &soundFinalWave[0], 2*735 }, + { NULL, 0 } + }; + +variable_desc soundSaveStructV2[] = { + { &sound3WaveRam[0], 0x20 }, + { &sound3Bank, sizeof(int) }, + { &sound3DataSize, sizeof(int) }, + { &sound3ForcedOutput, sizeof(int) }, + { NULL, 0 } + }; + +void soundEvent(u32 address, u8 data) +{ + int freq = 0; + + switch(address) + { + case NR10: + data &= 0x7f; + sound1SweepATL = sound1SweepATLReload = 344 * ((data >> 4) & 7); + sound1SweepSteps = data & 7; + sound1SweepUpDown = data & 0x08; + sound1SweepStep = 0; + ioMem[address] = data; + break; + case NR11: + sound1Wave = soundWavePattern[data >> 6]; + sound1ATL = 172 * (64 - (data & 0x3f)); + ioMem[address] = data; + break; + case NR12: + sound1EnvelopeUpDown = data & 0x08; + sound1EnvelopeATLReload = 689 * (data & 7); + if((data & 0xF8) == 0) + sound1EnvelopeVolume = 0; + ioMem[address] = data; + break; + case NR13: + freq = (((int)(ioMem[NR14] & 7)) << 8) | data; + sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); + freq = 2048 - freq; + if(freq) + { + sound1Skip = SOUND_MAGIC / freq; + } + else + sound1Skip = 0; + ioMem[address] = data; + break; + case NR14: + data &= 0xC7; + freq = (((int)(data&7) << 8) | ioMem[NR13]); + freq = 2048 - freq; + sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); + sound1Continue = data & 0x40; + if(freq) + { + sound1Skip = SOUND_MAGIC / freq; + } + else + sound1Skip = 0; + if(data & 0x80) + { + ioMem[NR52] |= 1; + sound1EnvelopeVolume = ioMem[NR12] >> 4; + sound1EnvelopeUpDown = ioMem[NR12] & 0x08; + sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); + sound1EnvelopeATLReload = sound1EnvelopeATL = 689 * (ioMem[NR12] & 7); + sound1SweepATL = sound1SweepATLReload = 344 * ((ioMem[NR10] >> 4) & 7); + sound1SweepSteps = ioMem[NR10] & 7; + sound1SweepUpDown = ioMem[NR10] & 0x08; + sound1SweepStep = 0; + + sound1Index = 0; + sound1On = 1; + } + ioMem[address] = data; + break; + case NR21: + sound2Wave = soundWavePattern[data >> 6]; + sound2ATL = 172 * (64 - (data & 0x3f)); + ioMem[address] = data; + break; + case NR22: + sound2EnvelopeUpDown = data & 0x08; + sound2EnvelopeATLReload = 689 * (data & 7); + if((data & 0xF8) == 0) + sound2EnvelopeVolume = 0; + ioMem[address] = data; + break; + case NR23: + freq = (((int)(ioMem[NR24] & 7)) << 8) | data; + sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); + freq = 2048 - freq; + if(freq) + { + sound2Skip = SOUND_MAGIC / freq; + } + else + sound2Skip = 0; + ioMem[address] = data; + break; + case NR24: + data &= 0xC7; + freq = (((int)(data&7) << 8) | ioMem[NR23]); + freq = 2048 - freq; + sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); + sound2Continue = data & 0x40; + if(freq) + { + sound2Skip = SOUND_MAGIC / freq; + } + else + sound2Skip = 0; + if(data & 0x80) + { + ioMem[NR52] |= 2; + sound2EnvelopeVolume = ioMem[NR22] >> 4; + sound2EnvelopeUpDown = ioMem[NR22] & 0x08; + sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); + sound2EnvelopeATLReload = sound2EnvelopeATL = 689 * (ioMem[NR22] & 7); + + sound2Index = 0; + sound2On = 1; + } + break; + ioMem[address] = data; + case NR30: + data &= 0xe0; + if(!(data & 0x80)) + { + ioMem[NR52] &= 0xfb; + sound3On = 0; + } + if(((data >> 6) & 1) != sound3Bank) + memcpy(&ioMem[0x90], &sound3WaveRam[(((data >> 6) & 1) * 0x10)^0x10], + 0x10); + sound3Bank = (data >> 6) & 1; + sound3DataSize = (data >> 5) & 1; + ioMem[address] = data; + break; + case NR31: + sound3ATL = 172 * (256-data); + ioMem[address] = data; + break; + case NR32: + data &= 0xe0; + sound3OutputLevel = (data >> 5) & 3; + sound3ForcedOutput = (data >> 7) & 1; + ioMem[address] = data; + break; + case NR33: + freq = 2048 - (((int)(ioMem[NR34]&7) << 8) | data); + if(freq) + { + sound3Skip = SOUND_MAGIC_2 / freq; + } + else + sound3Skip = 0; + ioMem[address] = data; + break; + case NR34: + data &= 0xc7; + freq = 2048 - (((data &7) << 8) | (int)ioMem[NR33]); + if(freq) + { + sound3Skip = SOUND_MAGIC_2 / freq; + } + else + { + sound3Skip = 0; + } + sound3Continue = data & 0x40; + if((data & 0x80) && (ioMem[NR30] & 0x80)) + { + ioMem[NR52] |= 4; + sound3ATL = 172 * (256 - ioMem[NR31]); + sound3Index = 0; + sound3On = 1; + } + ioMem[address] = data; + break; + case NR41: + data &= 0x3f; + sound4ATL = 172 * (64 - (data & 0x3f)); + ioMem[address] = data; + break; + case NR42: + sound4EnvelopeUpDown = data & 0x08; + sound4EnvelopeATLReload = 689 * (data & 7); + if((data & 0xF8) == 0) + sound4EnvelopeVolume = 0; + ioMem[address] = data; + break; + case NR43: + freq = soundFreqRatio[data & 7]; + sound4NSteps = data & 0x08; + + sound4Skip = (freq << 8) / NOISE_MAGIC; + + sound4Clock = data >> 4; + + freq = freq / soundShiftClock[sound4Clock]; + + sound4ShiftSkip = (freq << 8) / NOISE_MAGIC; + ioMem[address] = data; + break; + case NR44: + data &= 0xc0; + sound4Continue = data & 0x40; + if(data & 0x80) + { + ioMem[NR52] |= 8; + sound4EnvelopeVolume = ioMem[NR42] >> 4; + sound4EnvelopeUpDown = ioMem[NR42] & 0x08; + sound4ATL = 172 * (64 - (ioMem[NR41] & 0x3f)); + sound4EnvelopeATLReload = sound4EnvelopeATL = 689 * (ioMem[NR42] & 7); + + sound4On = 1; + + sound4Index = 0; + sound4ShiftIndex = 0; + + freq = soundFreqRatio[ioMem[NR43] & 7]; + + sound4Skip = (freq << 8) / NOISE_MAGIC; + + sound4NSteps = ioMem[NR43] & 0x08; + + freq = freq / soundShiftClock[ioMem[NR43] >> 4]; + + sound4ShiftSkip = (freq << 8) / NOISE_MAGIC; + if(sound4NSteps) + sound4ShiftRight = 0x7fff; + else + sound4ShiftRight = 0x7f; + } + ioMem[address] = data; + break; + case NR50: + data &= 0x77; + soundLevel1 = data & 7; + soundLevel2 = (data >> 4) & 7; + ioMem[address] = data; + break; + case NR51: + soundBalance = (data & soundEnableFlag); + ioMem[address] = data; + break; + case NR52: + data &= 0x80; + data |= ioMem[NR52] & 15; + soundMasterOn = data & 0x80; + if(!(data & 0x80)) + { + sound1On = 0; + sound2On = 0; + sound3On = 0; + sound4On = 0; + } + ioMem[address] = data; + break; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + sound3WaveRam[(sound3Bank*0x10)^0x10+(address&15)] = data; + break; + } +} + +void soundEvent(u32 address, u16 data) +{ + switch(address) + { + case SGCNT0_H: + data &= 0xFF0F; + soundControl = data & 0x770F; + ; + if(data & 0x0800) + { + soundDSFifoAWriteIndex = 0; + soundDSFifoAIndex = 0; + soundDSFifoACount = 0; + soundDSAValue = 0; + memset(soundDSFifoA, 0, 32); + } + soundDSAEnabled = (data & 0x0300) ? true : false; + soundDSATimer = (data & 0x0400) ? 1 : 0; + if(data & 0x8000) + { + soundDSFifoBWriteIndex = 0; + soundDSFifoBIndex = 0; + soundDSFifoBCount = 0; + soundDSBValue = 0; + memset(soundDSFifoB, 0, 32); + } + soundDSBEnabled = (data & 0x3000) ? true : false; + soundDSBTimer = (data & 0x4000) ? 1 : 0; + *((u16 *)&ioMem[address]) = data; + break; + case FIFOA_L: + case FIFOA_H: + soundDSFifoA[soundDSFifoAWriteIndex++] = data & 0xFF; + soundDSFifoA[soundDSFifoAWriteIndex++] = data >> 8; + soundDSFifoACount += 2; + soundDSFifoAWriteIndex &= 31; + *((u16 *)&ioMem[address]) = data; + break; + case FIFOB_L: + case FIFOB_H: + soundDSFifoB[soundDSFifoBWriteIndex++] = data & 0xFF; + soundDSFifoB[soundDSFifoBWriteIndex++] = data >> 8; + soundDSFifoBCount += 2; + soundDSFifoBWriteIndex &= 31; + *((u16 *)&ioMem[address]) = data; + break; + case 0x88: + data &= 0xC3FF; + *((u16 *)&ioMem[address]) = data; + break; + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + *((u16 *)&sound3WaveRam[(sound3Bank*0x10)^0x10+(address&14)]) = data; + *((u16 *)&ioMem[address]) = data; + break; + } +} + +void soundChannel1() +{ + int vol = sound1EnvelopeVolume; + + int freq = 0; + int value = 0; + + if(sound1On && (sound1ATL || !sound1Continue)) + { + sound1Index += soundQuality*sound1Skip; + sound1Index &= 0x1fffffff; + + value = ((s8)sound1Wave[sound1Index>>24]) * vol; + } + + soundBuffer[0][soundIndex] = value; + + + if(sound1On) + { + if(sound1ATL) + { + sound1ATL-=soundQuality; + + if(sound1ATL <=0 && sound1Continue) + { + ioMem[NR52] &= 0xfe; + sound1On = 0; + } + } + + if(sound1EnvelopeATL) + { + sound1EnvelopeATL-=soundQuality; + + if(sound1EnvelopeATL<=0) + { + if(sound1EnvelopeUpDown) + { + if(sound1EnvelopeVolume < 15) + sound1EnvelopeVolume++; + } + else + { + if(sound1EnvelopeVolume) + sound1EnvelopeVolume--; + } + + sound1EnvelopeATL += sound1EnvelopeATLReload; + } + } + + if(sound1SweepATL) + { + sound1SweepATL-=soundQuality; + + if(sound1SweepATL<=0) + { + freq = (((int)(ioMem[NR14]&7) << 8) | ioMem[NR13]); + + int updown = 1; + + if(sound1SweepUpDown) + updown = -1; + + int newfreq = 0; + if(sound1SweepSteps) + { + newfreq = freq + updown * freq / (1 << sound1SweepSteps); + if(newfreq == freq) + newfreq = 0; + } + else + newfreq = freq; + + if(newfreq < 0) + { + sound1SweepATL += sound1SweepATLReload; + } + else if(newfreq > 2047) + { + sound1SweepATL = 0; + sound1On = 0; + ioMem[NR52] &= 0xfe; + } + else + { + sound1SweepATL += sound1SweepATLReload; + sound1Skip = SOUND_MAGIC/(2048 - newfreq); + + ioMem[NR13] = newfreq & 0xff; + ioMem[NR14] = (ioMem[NR14] & 0xf8) |((newfreq >> 8) & 7); + } + } + } + } +} + +void soundChannel2() +{ + // int freq = 0; + int vol = sound2EnvelopeVolume; + + int value = 0; + + if(sound2On && (sound2ATL || !sound2Continue)) + { + sound2Index += soundQuality*sound2Skip; + sound2Index &= 0x1fffffff; + + value = ((s8)sound2Wave[sound2Index>>24]) * vol; + } + + soundBuffer[1][soundIndex] = value; + + if(sound2On) + { + if(sound2ATL) + { + sound2ATL-=soundQuality; + + if(sound2ATL <= 0 && sound2Continue) + { + ioMem[NR52] &= 0xfd; + sound2On = 0; + } + } + + if(sound2EnvelopeATL) + { + sound2EnvelopeATL-=soundQuality; + + if(sound2EnvelopeATL <= 0) + { + if(sound2EnvelopeUpDown) + { + if(sound2EnvelopeVolume < 15) + sound2EnvelopeVolume++; + } + else + { + if(sound2EnvelopeVolume) + sound2EnvelopeVolume--; + } + sound2EnvelopeATL += sound2EnvelopeATLReload; + } + } + } +} + +void soundChannel3() +{ + int value = sound3Last; + + if(sound3On && (sound3ATL || !sound3Continue)) + { + sound3Index += soundQuality*sound3Skip; + if(sound3DataSize) + { + sound3Index &= 0x3fffffff; + value = sound3WaveRam[sound3Index>>25]; + } + else + { + sound3Index &= 0x1fffffff; + value = sound3WaveRam[sound3Bank*0x10 + (sound3Index>>25)]; + } + + if( (sound3Index & 0x01000000)) + { + value &= 0x0f; + } + else + { + value >>= 4; + } + + value -= 8; + //value *= 2; + value <<= 1; + + if(sound3ForcedOutput) + { + value = ((value >> 1) + value) >> 1; + } + else + { + switch(sound3OutputLevel) + { + case 0: + value = 0; + break; + case 1: + break; + case 2: + value = (value >> 1); + break; + case 3: + value = (value >> 2); + break; + } + } + sound3Last = value; + } + + soundBuffer[2][soundIndex] = value; + + if(sound3On) + { + if(sound3ATL) + { + sound3ATL-=soundQuality; + + if(sound3ATL <= 0 && sound3Continue) + { + ioMem[NR52] &= 0xfb; + sound3On = 0; + } + } + } +} + +void soundChannel4() +{ + int vol = sound4EnvelopeVolume; + + int value = 0; + + if(sound4Clock <= 0x0c) + { + if(sound4On && (sound4ATL || !sound4Continue)) + { + sound4Index += soundQuality*sound4Skip; + sound4ShiftIndex += soundQuality*sound4ShiftSkip; + + if(sound4NSteps) + { + while(sound4ShiftIndex > 0x1fffff) + { + sound4ShiftRight = (((sound4ShiftRight << 6) ^ + (sound4ShiftRight << 5)) & 0x40) | + (sound4ShiftRight >> 1); + sound4ShiftIndex -= 0x200000; + } + } + else + { + while(sound4ShiftIndex > 0x1fffff) + { + sound4ShiftRight = (((sound4ShiftRight << 14) ^ + (sound4ShiftRight << 13)) & 0x4000) | + (sound4ShiftRight >> 1); + + sound4ShiftIndex -= 0x200000; + } + } + + sound4Index &= 0x1fffff; + sound4ShiftIndex &= 0x1fffff; + + value = ((sound4ShiftRight & 1)*2-1) * vol; + } + else + { + value = 0; + } + } + + soundBuffer[3][soundIndex] = value; + + if(sound4On) + { + if(sound4ATL) + { + sound4ATL-=soundQuality; + + if(sound4ATL <= 0 && sound4Continue) + { + ioMem[NR52] &= 0xfd; + sound4On = 0; + } + } + + if(sound4EnvelopeATL) + { + sound4EnvelopeATL-=soundQuality; + + if(sound4EnvelopeATL <= 0) + { + if(sound4EnvelopeUpDown) + { + if(sound4EnvelopeVolume < 15) + sound4EnvelopeVolume++; + } + else + { + if(sound4EnvelopeVolume) + sound4EnvelopeVolume--; + } + sound4EnvelopeATL += sound4EnvelopeATLReload; + } + } + } +} + +void soundDirectSoundA() +{ + soundBuffer[4][soundIndex] = soundDSAValue; +} + +void soundDirectSoundATimer() +{ + if(soundDSAEnabled) + { + if(soundDSFifoACount <= 16) + { + CPUCheckDMA(3, 2); + if(soundDSFifoACount <= 16) + { + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + } + } + + soundDSAValue = (soundDSFifoA[soundDSFifoAIndex]); + soundDSFifoAIndex = (++soundDSFifoAIndex) & 31; + soundDSFifoACount--; + } + else + soundDSAValue = 0; +} + +void soundDirectSoundB() +{ + soundBuffer[5][soundIndex] = soundDSBValue; +} + +void soundDirectSoundBTimer() +{ + if(soundDSBEnabled) + { + if(soundDSFifoBCount <= 16) + { + CPUCheckDMA(3, 4); + if(soundDSFifoBCount <= 16) + { + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + } + } + + soundDSBValue = (soundDSFifoB[soundDSFifoBIndex]); + soundDSFifoBIndex = (++soundDSFifoBIndex) & 31; + soundDSFifoBCount--; + } + else + { + soundDSBValue = 0; + } +} + +void soundTimerOverflow(int timer) +{ + if(soundDSAEnabled && (soundDSATimer == timer)) + { + soundDirectSoundATimer(); + } + if(soundDSBEnabled && (soundDSBTimer == timer)) + { + soundDirectSoundBTimer(); + } +} + +#ifndef max +#define max(a,b) (a)<(b)?(b):(a) +#endif + +void soundMix() +{ + int res = 0; + int cgbRes = 0; + int ratio = ioMem[0x82] & 3; + int dsaRatio = ioMem[0x82] & 4; + int dsbRatio = ioMem[0x82] & 8; + + if(soundBalance & 16) + { + cgbRes = ((s8)soundBuffer[0][soundIndex]); + } + if(soundBalance & 32) + { + cgbRes += ((s8)soundBuffer[1][soundIndex]); + } + if(soundBalance & 64) + { + cgbRes += ((s8)soundBuffer[2][soundIndex]); + } + if(soundBalance & 128) + { + cgbRes += ((s8)soundBuffer[3][soundIndex]); + } + + if((soundControl & 0x0200) && (soundEnableFlag & 0x100)) + { + if(!dsaRatio) + res = ((s8)soundBuffer[4][soundIndex])>>1; + else + res = ((s8)soundBuffer[4][soundIndex]); + } + + if((soundControl & 0x2000) && (soundEnableFlag & 0x200)) + { + if(!dsbRatio) + res += ((s8)soundBuffer[5][soundIndex])>>1; + else + res += ((s8)soundBuffer[5][soundIndex]); + } + + res = (res * 170); + cgbRes = (cgbRes * 52 * soundLevel1); + + switch(ratio) + { + case 0: + case 3: // prohibited, but 25% + cgbRes >>= 2; + break; + case 1: + cgbRes >>= 1; + break; + case 2: + break; + } + + res += cgbRes; + + if(soundEcho) + { + res *= 2; + res += soundFilter[soundEchoIndex]; + res /= 2; + soundFilter[soundEchoIndex++] = res; + } + + if(soundLowPass) + { + soundLeft[4] = soundLeft[3]; + soundLeft[3] = soundLeft[2]; + soundLeft[2] = soundLeft[1]; + soundLeft[1] = soundLeft[0]; + soundLeft[0] = res; + res = (soundLeft[4] + 2*soundLeft[3] + 8*soundLeft[2] + 2*soundLeft[1] + + soundLeft[0])/14; + } + + switch(soundVolume) + { + case 0: + case 1: + case 2: + case 3: + res *= (soundVolume+1); + break; + case 4: + res >>= 2; + break; + case 5: + res >>= 1; + break; + } + + if(res > 32767) + res = 32767; + if(res < -32768) + res = -32768; + + if(soundReverse) + soundFinalWave[++soundBufferIndex] = res; + else + soundFinalWave[soundBufferIndex++] = res; + + res = 0; + cgbRes = 0; + + if(soundBalance & 1) + { + cgbRes = ((s8)soundBuffer[0][soundIndex]); + } + if(soundBalance & 2) + { + cgbRes += ((s8)soundBuffer[1][soundIndex]); + } + if(soundBalance & 4) + { + cgbRes += ((s8)soundBuffer[2][soundIndex]); + } + if(soundBalance & 8) + { + cgbRes += ((s8)soundBuffer[3][soundIndex]); + } + + if((soundControl & 0x0100) && (soundEnableFlag & 0x100)) + { + if(!dsaRatio) + res = ((s8)soundBuffer[4][soundIndex])>>1; + else + res = ((s8)soundBuffer[4][soundIndex]); + } + + if((soundControl & 0x1000) && (soundEnableFlag & 0x200)) + { + if(!dsbRatio) + res += ((s8)soundBuffer[5][soundIndex])>>1; + else + res += ((s8)soundBuffer[5][soundIndex]); + } + + res = (res * 170); + cgbRes = (cgbRes * 52 * soundLevel1); + + switch(ratio) + { + case 0: + case 3: // prohibited, but 25% + cgbRes >>= 2; + break; + case 1: + cgbRes >>= 1; + break; + case 2: + break; + } + + res += cgbRes; + + if(soundEcho) + { + res *= 2; + res += soundFilter[soundEchoIndex]; + res /= 2; + soundFilter[soundEchoIndex++] = res; + + if(soundEchoIndex >= 4000) + soundEchoIndex = 0; + } + + if(soundLowPass) + { + soundRight[4] = soundRight[3]; + soundRight[3] = soundRight[2]; + soundRight[2] = soundRight[1]; + soundRight[1] = soundRight[0]; + soundRight[0] = res; + res = (soundRight[4] + 2*soundRight[3] + 8*soundRight[2] + 2*soundRight[1] + + soundRight[0])/14; + } + + switch(soundVolume) + { + case 0: + case 1: + case 2: + case 3: + res *= (soundVolume+1); + break; + case 4: + res >>= 2; + break; + case 5: + res >>= 1; + break; + } + + if(res > 32767) + res = 32767; + if(res < -32768) + res = -32768; + + if(soundReverse) + soundFinalWave[-1+soundBufferIndex++] = res; + else + soundFinalWave[soundBufferIndex++] = res; +} + +void soundTick() +{ + if(systemSoundOn) + { + if(soundMasterOn && !stopState) + { + soundChannel1(); + soundChannel2(); + soundChannel3(); + soundChannel4(); + soundDirectSoundA(); + soundDirectSoundB(); + soundMix(); + } + else + { + soundFinalWave[soundBufferIndex++] = 0; + soundFinalWave[soundBufferIndex++] = 0; + } + + soundIndex++; + + if(2*soundBufferIndex >= soundBufferLen) + { + if(systemSoundOn) + { + if(soundPaused) + { + soundResume(); + } + + systemWriteDataToSoundBuffer(); + } + soundIndex = 0; + soundBufferIndex = 0; + } + } +} + +void soundShutdown() +{ + systemSoundShutdown(); +} + +void soundPause() +{ + systemSoundPause(); + soundPaused = 1; +} + +void soundResume() +{ + systemSoundResume(); + soundPaused = 0; +} + +void soundEnable(int channels) +{ + int c = channels & 0x0f; + + soundEnableFlag |= ((channels & 0x30f) |c | (c << 4)); + if(ioMem) + soundBalance = (ioMem[NR51] & soundEnableFlag); +} + +void soundDisable(int channels) +{ + int c = channels & 0x0f; + + soundEnableFlag &= (~((channels & 0x30f)|c|(c<<4))); + if(ioMem) + soundBalance = (ioMem[NR51] & soundEnableFlag); +} + +int soundGetEnable() +{ + return (soundEnableFlag & 0x30f); +} + +void soundReset() +{ + systemSoundReset(); + + soundPaused = 1; + soundPlay = 0; + SOUND_CLOCK_TICKS = soundQuality * USE_TICKS_AS; + soundTicks = SOUND_CLOCK_TICKS; + soundNextPosition = 0; + soundMasterOn = 1; + soundIndex = 0; + soundBufferIndex = 0; + soundLevel1 = 7; + soundLevel2 = 7; + + sound1On = 0; + sound1ATL = 0; + sound1Skip = 0; + sound1Index = 0; + sound1Continue = 0; + sound1EnvelopeVolume = 0; + sound1EnvelopeATL = 0; + sound1EnvelopeUpDown = 0; + sound1EnvelopeATLReload = 0; + sound1SweepATL = 0; + sound1SweepATLReload = 0; + sound1SweepSteps = 0; + sound1SweepUpDown = 0; + sound1SweepStep = 0; + sound1Wave = soundWavePattern[2]; + + sound2On = 0; + sound2ATL = 0; + sound2Skip = 0; + sound2Index = 0; + sound2Continue = 0; + sound2EnvelopeVolume = 0; + sound2EnvelopeATL = 0; + sound2EnvelopeUpDown = 0; + sound2EnvelopeATLReload = 0; + sound2Wave = soundWavePattern[2]; + + sound3On = 0; + sound3ATL = 0; + sound3Skip = 0; + sound3Index = 0; + sound3Continue = 0; + sound3OutputLevel = 0; + sound3Last = 0; + sound3Bank = 0; + sound3DataSize = 0; + sound3ForcedOutput = 0; + + sound4On = 0; + sound4Clock = 0; + sound4ATL = 0; + sound4Skip = 0; + sound4Index = 0; + sound4ShiftRight = 0x7f; + sound4NSteps = 0; + sound4CountDown = 0; + sound4Continue = 0; + sound4EnvelopeVolume = 0; + sound4EnvelopeATL = 0; + sound4EnvelopeUpDown = 0; + sound4EnvelopeATLReload = 0; + + sound1On = 0; + sound2On = 0; + sound3On = 0; + sound4On = 0; + + int addr = 0x90; + + while(addr < 0xA0) + { + ioMem[addr++] = 0x00; + ioMem[addr++] = 0xff; + } + + addr = 0; + while(addr < 0x20) + { + sound3WaveRam[addr++] = 0x00; + sound3WaveRam[addr++] = 0xff; + } + + memset(soundFinalWave, 0, soundBufferLen); + + memset(soundFilter, 0, sizeof(soundFilter)); + soundEchoIndex = 0; +} + +bool soundInit() +{ + if(systemSoundInit()) + { + memset(soundBuffer[0], 0, 735*2); + memset(soundBuffer[1], 0, 735*2); + memset(soundBuffer[2], 0, 735*2); + memset(soundBuffer[3], 0, 735*2); + + memset(soundFinalWave, 0, soundBufferLen); + + soundPaused = true; + return true; + } + return false; +} + +void soundSetQuality(int quality) +{ + if(soundQuality != quality && systemCanChangeSoundQuality()) + { + if(!soundOffFlag) + soundShutdown(); + soundQuality = quality; + soundNextPosition = 0; + if(!soundOffFlag) + soundInit(); + SOUND_CLOCK_TICKS = USE_TICKS_AS * soundQuality; + soundIndex = 0; + soundBufferIndex = 0; + } + else if(soundQuality != quality) + { + soundNextPosition = 0; + SOUND_CLOCK_TICKS = USE_TICKS_AS * soundQuality; + soundIndex = 0; + soundBufferIndex = 0; + } +} + +void soundSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, soundSaveStruct); + utilWriteData(gzFile, soundSaveStructV2); + + utilGzWrite(gzFile, &soundQuality, sizeof(int)); +} + +void soundReadGame(gzFile gzFile, int version) +{ + utilReadData(gzFile, soundSaveStruct); + if(version >= SAVE_GAME_VERSION_3) + { + utilReadData(gzFile, soundSaveStructV2); + } + else + { + sound3Bank = (ioMem[NR30] >> 6) & 1; + sound3DataSize = (ioMem[NR30] >> 5) & 1; + sound3ForcedOutput = (ioMem[NR32] >> 7) & 1; + // nothing better to do here... + memcpy(&sound3WaveRam[0x00], &ioMem[0x90], 0x10); + memcpy(&sound3WaveRam[0x10], &ioMem[0x90], 0x10); + } + soundBufferIndex = soundIndex * 2; + + int quality = 1; + utilGzRead(gzFile, &quality, sizeof(int)); + soundSetQuality(quality); + + sound1Wave = soundWavePattern[ioMem[NR11] >> 6]; + sound2Wave = soundWavePattern[ioMem[NR21] >> 6]; +} diff --git a/src/Sound.h b/src/Sound.h new file mode 100644 index 0000000..4e1dac7 --- /dev/null +++ b/src/Sound.h @@ -0,0 +1,82 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_SOUND_H +#define VBA_SOUND_H + +#define NR10 0x60 +#define NR11 0x62 +#define NR12 0x63 +#define NR13 0x64 +#define NR14 0x65 +#define NR21 0x68 +#define NR22 0x69 +#define NR23 0x6c +#define NR24 0x6d +#define NR30 0x70 +#define NR31 0x72 +#define NR32 0x73 +#define NR33 0x74 +#define NR34 0x75 +#define NR41 0x78 +#define NR42 0x79 +#define NR43 0x7c +#define NR44 0x7d +#define NR50 0x80 +#define NR51 0x81 +#define NR52 0x84 +#define SGCNT0_H 0x82 +#define FIFOA_L 0xa0 +#define FIFOA_H 0xa2 +#define FIFOB_L 0xa4 +#define FIFOB_H 0xa6 + +extern void soundTick(); +extern void soundShutdown(); +extern bool soundInit(); +extern void soundPause(); +extern void soundResume(); +extern void soundEnable(int); +extern void soundDisable(int); +extern int soundGetEnable(); +extern void soundReset(); +extern void soundSaveGame(gzFile); +extern void soundReadGame(gzFile, int); +extern void soundEvent(u32, u8); +extern void soundEvent(u32, u16); +extern void soundTimerOverflow(int); +extern void soundSetQuality(int); + +//extern int SOUND_TICKS; +extern int SOUND_CLOCK_TICKS; +extern int soundTicks; +extern int soundPaused; +extern bool soundOffFlag; +extern int soundQuality; +extern int soundBufferLen; +extern int soundBufferTotalLen; +extern u32 soundNextPosition; +extern u16 soundFinalWave[1470]; +extern int soundVolume; + +extern bool soundEcho; +extern bool soundLowPass; +extern bool soundReverse; + +#endif // VBA_SOUND_H diff --git a/src/Sram.cpp b/src/Sram.cpp new file mode 100644 index 0000000..1a46097 --- /dev/null +++ b/src/Sram.cpp @@ -0,0 +1,32 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "GBA.h" +#include "Flash.h" +#include "Sram.h" + +u8 sramRead(u32 address) +{ + return flashSaveMemory[address & 0xFFFF]; +} + +void sramWrite(u32 address, u8 byte) +{ + flashSaveMemory[address & 0xFFFF] = byte; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; +} diff --git a/src/Sram.h b/src/Sram.h new file mode 100644 index 0000000..f33f4c8 --- /dev/null +++ b/src/Sram.h @@ -0,0 +1,26 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_SRAM_H +#define VBA_SRAM_H + +extern u8 sramRead(u32 address); +extern void sramWrite(u32 address, u8 byte); + +#endif // VBA_SRAM_H diff --git a/src/System.h b/src/System.h new file mode 100644 index 0000000..c58a0fb --- /dev/null +++ b/src/System.h @@ -0,0 +1,127 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_SYSTEM_H +#define VBA_SYSTEM_H + +#include "unzip.h" + +#ifndef NULL +#define NULL 0 +#endif + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +#ifdef _MSC_VER +typedef unsigned __int64 u64; +#else +typedef unsigned long long u64; +#endif + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; + +#ifdef _MSC_VER +typedef signed __int64 s64; +#else +typedef signed long long s64; +#endif + +struct EmulatedSystem + { + // main emulation function + void (*emuMain)(int); + // reset emulator + void (*emuReset)(); + // clean up memory + void (*emuCleanUp)(); + // load battery file + bool (*emuReadBattery)(const char *); + // write battery file + bool (*emuWriteBattery)(const char *); + // load state + bool (*emuReadState)(const char *); + // save state + bool (*emuWriteState)(const char *); + // load memory state (rewind) + bool (*emuReadMemState)(char *, int); + // write memory state (rewind) + bool (*emuWriteMemState)(char *, int); + // write PNG file + bool (*emuWritePNG)(const char *); + // write BMP file + bool (*emuWriteBMP)(const char *); + // emulator update CPSR (ARM only) + void (*emuUpdateCPSR)(); + // emulator has debugger + bool emuHasDebugger; + // clock ticks to emulate + int emuCount; + }; + +extern void log(const char *,...); + +extern bool systemPauseOnFrame(); +extern void systemGbPrint(u8 *,int,int,int,int); +extern void systemScreenCapture(int); +extern void systemDrawScreen(); +// updates the joystick data +extern bool systemReadJoypads(); +// return information about the given joystick, -1 for default joystick +extern u32 systemReadJoypad(int); +extern u32 systemGetClock(); +extern void systemMessage(int, const char *, ...); +extern void systemSetTitle(const char *); +extern void systemWriteDataToSoundBuffer(); +extern void systemSoundShutdown(); +extern void systemSoundPause(); +extern void systemSoundResume(); +extern void systemSoundReset(); +extern bool systemSoundInit(); +extern void systemScreenMessage(const char *); +extern void systemUpdateMotionSensor(); +extern int systemGetSensorX(); +extern int systemGetSensorY(); +extern bool systemCanChangeSoundQuality(); +extern void systemShowSpeed(int); +extern void system10Frames(int); +extern void systemFrame(); +extern void systemGbBorderOn(); + +extern bool systemSoundOn; +extern u16 systemColorMap16[0x10000]; +//extern u32 systemColorMap32[0x10000]; +extern u32 *systemColorMap32; +extern u16 systemGbPalette[24]; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; +extern int systemColorDepth; +extern int systemDebug; +extern int systemVerbose; +extern int systemFrameSkip; +extern int systemSaveUpdateCounter; + +#define SYSTEM_SAVE_UPDATED 30 +#define SYSTEM_SAVE_NOT_UPDATED 0 + +#endif //VBA_SYSTEM_H diff --git a/src/Text.cpp b/src/Text.cpp new file mode 100644 index 0000000..e9925ea --- /dev/null +++ b/src/Text.cpp @@ -0,0 +1,180 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * 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 2 of the License, or + * (at your option) any later version. + * + * 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Code originally from fceu/drawing.h file, adapted by Forgotten + */ +#include "System.h" + +extern int RGB_LOW_BITS_MASK; + +static const u8 fontdata2[2048] = + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e,0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e,0x36,0x7f,0x7f,0x7f,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00,0x1c,0x3e,0x1c,0x7f,0x7f,0x3e,0x1c,0x3e,0x08,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x3e,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xf0,0xe0,0xf0,0xbe,0x33,0x33,0x33,0x1e,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0xfc,0xcc,0xfc,0x0c,0x0c,0x0e,0x0f,0x07,0xfe,0xc6,0xfe,0xc6,0xc6,0xe6,0x67,0x03,0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99,0x01,0x07,0x1f,0x7f,0x1f,0x07,0x01,0x00,0x40,0x70,0x7c,0x7f,0x7c,0x70,0x40,0x00,0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00,0xfe,0xdb,0xdb,0xde,0xd8,0xd8,0xd8,0x00,0x7c,0xc6,0x1c,0x36,0x36,0x1c,0x33,0x1e,0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00,0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x18,0x30,0x7f,0x30,0x18,0x00,0x00,0x00,0x0c,0x06,0x7f,0x06,0x0c,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x7f,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x1e,0x0c,0x0c,0x00,0x0c,0x00,0x36,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x7f,0x36,0x7f,0x36,0x36,0x00,0x0c,0x3e,0x03,0x1e,0x30,0x1f,0x0c,0x00,0x00,0x63,0x33,0x18,0x0c,0x66,0x63,0x00,0x1c,0x36,0x1c,0x6e,0x3b,0x33,0x6e,0x00,0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0x06,0x06,0x06,0x0c,0x18,0x00,0x06,0x0c,0x18,0x18,0x18,0x0c,0x06,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,0x3e,0x63,0x73,0x7b,0x6f,0x67,0x3e,0x00,0x0c,0x0e,0x0c,0x0c,0x0c,0x0c,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x06,0x33,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x30,0x33,0x1e,0x00,0x38,0x3c,0x36,0x33,0x7f,0x30,0x78,0x00,0x3f,0x03,0x1f,0x30,0x30,0x33,0x1e,0x00,0x1c,0x06,0x03,0x1f,0x33,0x33,0x1e,0x00,0x3f,0x33,0x30,0x18,0x0c,0x0c,0x0c,0x00,0x1e,0x33,0x33,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x33,0x3e,0x30,0x18,0x0e,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x06,0x18,0x0c,0x06,0x03,0x06,0x0c,0x18,0x00,0x00,0x00,0x3f,0x00,0x00,0x3f,0x00,0x00,0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x1e,0x33,0x30,0x18,0x0c,0x00,0x0c,0x00, + 0x3e,0x63,0x7b,0x7b,0x7b,0x03,0x1e,0x00,0x0c,0x1e,0x33,0x33,0x3f,0x33,0x33,0x00,0x3f,0x66,0x66,0x3e,0x66,0x66,0x3f,0x00,0x3c,0x66,0x03,0x03,0x03,0x66,0x3c,0x00,0x1f,0x36,0x66,0x66,0x66,0x36,0x1f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x46,0x7f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x06,0x0f,0x00,0x3c,0x66,0x03,0x03,0x73,0x66,0x7c,0x00,0x33,0x33,0x33,0x3f,0x33,0x33,0x33,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x78,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x67,0x66,0x36,0x1e,0x36,0x66,0x67,0x00,0x0f,0x06,0x06,0x06,0x46,0x66,0x7f,0x00,0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00,0x63,0x67,0x6f,0x7b,0x73,0x63,0x63,0x00,0x1c,0x36,0x63,0x63,0x63,0x36,0x1c,0x00,0x3f,0x66,0x66,0x3e,0x06,0x06,0x0f,0x00,0x1e,0x33,0x33,0x33,0x3b,0x1e,0x38,0x00,0x3f,0x66,0x66,0x3e,0x36,0x66,0x67,0x00,0x1e,0x33,0x07,0x0e,0x38,0x33,0x1e,0x00,0x3f,0x2d,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0x3f,0x00,0x33,0x33,0x33,0x33,0x33,0x1e,0x0c,0x00,0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00,0x33,0x33,0x33,0x1e,0x0c,0x0c,0x1e,0x00,0x7f,0x63,0x31,0x18,0x4c,0x66,0x7f,0x00,0x1e,0x06,0x06,0x06,0x06,0x06,0x1e,0x00,0x03,0x06,0x0c,0x18,0x30,0x60,0x40,0x00,0x1e,0x18,0x18,0x18,0x18,0x18,0x1e,0x00,0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0x0c,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0x30,0x3e,0x33,0x6e,0x00,0x07,0x06,0x06,0x3e,0x66,0x66,0x3b,0x00,0x00,0x00,0x1e,0x33,0x03,0x33,0x1e,0x00,0x38,0x30,0x30,0x3e,0x33,0x33,0x6e,0x00,0x00,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x1c,0x36,0x06,0x0f,0x06,0x06,0x0f,0x00,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x1f,0x07,0x06,0x36,0x6e,0x66,0x66,0x67,0x00,0x0c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1e,0x07,0x06,0x66,0x36,0x1e,0x36,0x67,0x00,0x0e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x33,0x7f,0x7f,0x6b,0x63,0x00,0x00,0x00,0x1f,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x1e,0x33,0x33,0x33,0x1e,0x00,0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78,0x00,0x00,0x3b,0x6e,0x66,0x06,0x0f,0x00,0x00,0x00,0x3e,0x03,0x1e,0x30,0x1f,0x00,0x08,0x0c,0x3e,0x0c,0x0c,0x2c,0x18,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00,0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00,0x00,0x00,0x33,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x3f,0x19,0x0c,0x26,0x3f,0x00,0x38,0x0c,0x0c,0x07,0x0c,0x0c,0x38,0x00,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x07,0x0c,0x0c,0x38,0x0c,0x0c,0x07,0x00,0x6e,0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00, + 0x1e,0x33,0x03,0x33,0x1e,0x18,0x30,0x1e,0x00,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x38,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x7e,0xc3,0x3c,0x60,0x7c,0x66,0xfc,0x00,0x33,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x07,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x0c,0x0c,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x00,0x00,0x1e,0x03,0x03,0x1e,0x30,0x1c,0x7e,0xc3,0x3c,0x66,0x7e,0x06,0x3c,0x00,0x33,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x07,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x33,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x3e,0x63,0x1c,0x18,0x18,0x18,0x3c,0x00,0x07,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x63,0x1c,0x36,0x63,0x7f,0x63,0x63,0x00,0x0c,0x0c,0x00,0x1e,0x33,0x3f,0x33,0x00,0x38,0x00,0x3f,0x06,0x1e,0x06,0x3f,0x00,0x00,0x00,0xfe,0x30,0xfe,0x33,0xfe,0x00,0x7c,0x36,0x33,0x7f,0x33,0x33,0x73,0x00,0x1e,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x07,0x00,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x07,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x33,0x00,0x33,0x33,0x3e,0x30,0x1f,0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00,0x33,0x00,0x33,0x33,0x33,0x33,0x1e,0x00,0x18,0x18,0x7e,0x03,0x03,0x7e,0x18,0x18,0x1c,0x36,0x26,0x0f,0x06,0x67,0x3f,0x00,0x33,0x33,0x1e,0x3f,0x0c,0x3f,0x0c,0x0c,0x1f,0x33,0x33,0x5f,0x63,0xf3,0x63,0xe3,0x70,0xd8,0x18,0x3c,0x18,0x18,0x1b,0x0e, + 0x38,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x1c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x38,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x38,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x1f,0x00,0x1f,0x33,0x33,0x33,0x00,0x3f,0x00,0x33,0x37,0x3f,0x3b,0x33,0x00,0x3c,0x36,0x36,0x7c,0x00,0x7e,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x3e,0x00,0x00,0x0c,0x00,0x0c,0x06,0x03,0x33,0x1e,0x00,0x00,0x00,0x00,0x3f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x30,0x00,0x00,0xc3,0x63,0x33,0x7b,0xcc,0x66,0x33,0xf0,0xc3,0x63,0x33,0xdb,0xec,0xf6,0xf3,0xc0,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00,0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00,0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xdb,0xee,0xdb,0x77,0xdb,0xee,0xdb,0x77,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0x6f,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x7f,0x6c,0x6c,0x6c,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x7f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6f,0x60,0x7f,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0x7f,0x00,0x00,0x00,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xfc,0x00,0x00,0x00,0x00,0x00,0xfc,0x0c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xef,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xef,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xec,0x6c,0x6c,0x6c,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0xef,0x00,0xef,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xfc,0x00,0x00,0x00,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xfc,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xff,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x6e,0x3b,0x13,0x3b,0x6e,0x00,0x00,0x1e,0x33,0x1f,0x33,0x1f,0x03,0x03,0x00,0x3f,0x33,0x03,0x03,0x03,0x03,0x00,0x00,0x7f,0x36,0x36,0x36,0x36,0x36,0x00,0x3f,0x33,0x06,0x0c,0x06,0x33,0x3f,0x00,0x00,0x00,0x7e,0x1b,0x1b,0x1b,0x0e,0x00,0x00,0x66,0x66,0x66,0x66,0x3e,0x06,0x03,0x00,0x6e,0x3b,0x18,0x18,0x18,0x18,0x00,0x3f,0x0c,0x1e,0x33,0x33,0x1e,0x0c,0x3f,0x1c,0x36,0x63,0x7f,0x63,0x36,0x1c,0x00,0x1c,0x36,0x63,0x63,0x36,0x36,0x77,0x00,0x38,0x0c,0x18,0x3e,0x33,0x33,0x1e,0x00,0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00,0x60,0x30,0x7e,0xdb,0xdb,0x7e,0x06,0x03,0x1c,0x06,0x03,0x1f,0x03,0x06,0x1c,0x00,0x1e,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x3f,0x00,0x3f,0x00,0x3f,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x3f,0x00,0x06,0x0c,0x18,0x0c,0x06,0x00,0x3f,0x00,0x18,0x0c,0x06,0x0c,0x18,0x00,0x3f,0x00,0x70,0xd8,0xd8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x0e,0x0c,0x0c,0x00,0x3f,0x00,0x0c,0x0c,0x00,0x00,0x6e,0x3b,0x00,0x6e,0x3b,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xf0,0x30,0x30,0x30,0x37,0x36,0x3c,0x38,0x1e,0x36,0x36,0x36,0x36,0x00,0x00,0x00,0x0e,0x18,0x0c,0x06,0x1e,0x00,0x00,0x00,0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + +static void drawTextInternal(u8 *screen, int pitch, int x, int y, + const char *string, bool trans) +{ + screen += y*pitch; + int inc = 2; + switch(systemColorDepth) + { + case 24: + inc = 3; + break; + case 32: + inc = 4; + break; + } + screen += x*inc; + + switch(systemColorDepth) + { + case 16: + { + while(*string) + { + char c = *string++; + u8 *scr = screen; + + u16 mask = ~RGB_LOW_BITS_MASK; + int h, w; + u16 *s = (u16 *)scr; + for (h = 0; h < 8; h++) + { + for (w = 0; w < 8; w++, s++) + { + int on = (fontdata2[(c<<3)+h]>>w)&1; + + if(trans) + { + if(on) + *s = ((0xf) << systemRedShift) + + ((*s & mask) >>1); + } + else + { + if(on) + *s = (0x1f) << systemRedShift; + } + } + scr += pitch; + s = (u16 *)scr; + } + screen += inc*8; + } + } + break; + case 24: + { + while(*string) + { + char c = *string++; + u8 *scr = screen; + + int h, w; + u8 *s = (u8 *)scr; + for (h = 0; h < 8; h++) + { + for (w = 0; w < 8; w++, s+=3) + { + int on = (fontdata2[(c<<3)+h]>>w)&1; + + if(trans) + { + if(on) + { + u32 color = (0x1f) << systemRedShift; + *s = ((color & 255)>>1)+(*s>>1); + *(s+1) = (((color >> 8) & 255)>>1)+(*(s+1)>>1); + *(s+2) = (((color >> 16) & 255)>>1)+(*(s+2)>>1); + } + } + else + { + if(on) + { + u32 color = (0x1f) << systemRedShift; + *s = (color & 255); + *(s+1) = (color >> 8) & 255; + *(s+2) = (color >> 16) & 255; + } + } + } + scr += pitch; + s = (u8 *)scr; + } + screen += inc*8; + } + } + break; + case 32: + { + while(*string) + { + char c = *string++; + u8 *scr = screen; + + int h, w; + u32 mask = 0xfefefe; + u32 *s = (u32 *)scr; + for (h = 0; h < 8; h++) + { + for (w = 0; w < 8; w++, s++) + { + int on = (fontdata2[(c<<3)+h]>>w)&1; + + if(trans) + { + if(on) + *s = ((0xf) << systemRedShift) + ((*s & mask)>>1); + } + else + { + if(on) + *s = (0x1f) << systemRedShift; + } + } + scr += pitch; + s = (u32 *)scr; + } + screen += inc*8; + } + } + break; + } +} + +void drawText(u8 *screen, int pitch, int x, int y, const char *string) +{ + drawTextInternal(screen, pitch, x, y, string, false); +} + +void drawTextTransp(u8 *screen, int pitch, int x, int y, const char *string) +{ + drawTextInternal(screen, pitch, x, y, string, true); +} diff --git a/src/Text.h b/src/Text.h new file mode 100644 index 0000000..cbfe8be --- /dev/null +++ b/src/Text.h @@ -0,0 +1,21 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +extern void drawText(u8 *, int, int, int, const char *); +extern void drawTextTransp(u8 *, int, int, int, const char *); diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..2823e54 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,1196 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "sdfileio.h" +#include +#include +#include +#include + +extern "C" + { +#include + } + +#if 0 +#include "unrarlib.h" +#endif + +#include "System.h" +#include "NLS.h" +#include "Util.h" +#include "Flash.h" +#include "GBA.h" +#include "Globals.h" +#include "RTC.h" +#include "Port.h" + + +extern "C" + { +#include "memgzio.h" + } + +#ifndef _MSC_VER +#define _stricmp strcasecmp +#endif // ! _MSC_VER + +static int (*utilGzWriteFunc)(gzFile, const voidp, unsigned int) = NULL; +static int (*utilGzReadFunc)(gzFile, voidp, unsigned int) = NULL; +static int (*utilGzCloseFunc)(gzFile) = NULL; + +bool utilWritePNGFile(const char *fileName, int w, int h, u8 *pix) +{ +#if 0 + u8 writeBuffer[512 * 3]; + + FILE* fp = gen_fopen(fileName,"wb"); + + if(!fp) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), fileName); + return false; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) + { + gen_fclose(fp); + return false; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) + { + png_destroy_write_struct(&png_ptr,NULL); + gen_fclose(fp); + return false; + } + + if(setjmp(png_ptr->jmpbuf)) + { + png_destroy_write_struct(&png_ptr,NULL); + gen_fclose(fp); + return false; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + switch(systemColorDepth) + { + case 16: + { + u16 *p = (u16 *)(pix+(w+2)*2); // skip first black line + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + u16 v = *p++; + + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + } + break; + case 24: + { + u8 *pixU8 = (u8 *)pix; + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + if(systemRedShift < systemBlueShift) + { + *b++ = *pixU8++; // R + *b++ = *pixU8++; // G + *b++ = *pixU8++; // B + } + else + { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + } + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + } + break; + case 32: + { + u32 *pixU32 = (u32 *)(pix+4*(w+1)); + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + u32 v = *pixU32++; + + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + } + pixU32++; + + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + } + break; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + gen_fclose(fp); + +#endif + return true; +} + +void utilPutDword(u8 *p, u32 value) +{ + *p++ = value & 255; + *p++ = (value >> 8) & 255; + *p++ = (value >> 16) & 255; + *p = (value >> 24) & 255; +} + +void utilPutWord(u8 *p, u16 value) +{ + *p++ = value & 255; + *p = (value >> 8) & 255; +} + +void utilWriteBMP(char *buf, int w, int h, u8 *pix) +{ + u8 *b = (u8 *)buf; + + int sizeX = w; + int sizeY = h; + + switch(systemColorDepth) + { + case 16: + { + u16 *p = (u16 *)(pix+(w+2)*(h)*2); // skip first black line + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + u16 v = *p++; + + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + p -= 2*(w+2); + } + } + break; + case 24: + { + u8 *pixU8 = (u8 *)pix+3*w*(h-1); + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + if(systemRedShift > systemBlueShift) + { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + else + { + int red = *pixU8++; + int green = *pixU8++; + int blue = *pixU8++; + + *b++ = blue; + *b++ = green; + *b++ = red; + } + } + pixU8 -= 2*3*w; + } + } + break; + case 32: + { + u32 *pixU32 = (u32 *)(pix+4*(w+1)*(h)); + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + u32 v = *pixU32++; + + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + pixU32++; + pixU32 -= 2*(w+1); + } + } + break; + } +} + +bool utilWriteBMPFile(const char *fileName, int w, int h, u8 *pix) +{ + u8 writeBuffer[512 * 3]; + + FILE* fp = gen_fopen(fileName,"wb"); + + if(!fp) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), fileName); + return false; + } + + struct + { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + // u8 pad[2]; + } + bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w*h*3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x36); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3*w*h); + + gen_fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + switch(systemColorDepth) + { + case 16: + { + u16 *p = (u16 *)(pix+(w+2)*(h)*2); // skip first black line + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + u16 v = *p++; + + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + p -= 2*(w+2); + gen_fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + } + break; + case 24: + { + u8 *pixU8 = (u8 *)pix+3*w*(h-1); + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + if(systemRedShift > systemBlueShift) + { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + else + { + int red = *pixU8++; + int green = *pixU8++; + int blue = *pixU8++; + + *b++ = blue; + *b++ = green; + *b++ = red; + } + } + pixU8 -= 2*3*w; + gen_fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + } + break; + case 32: + { + u32 *pixU32 = (u32 *)(pix+4*(w+1)*(h)); + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + u32 v = *pixU32++; + + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + pixU32++; + pixU32 -= 2*(w+1); + + gen_fwrite(writeBuffer, 1, 3*w, fp); + + b = writeBuffer; + } + } + break; + } + + gen_fclose(fp); + + return true; +} + +static int utilReadInt2(FILE* f) +{ + int res = 0; + int c = gen_fgetc(f); + if(c == EOF) + return -1; + res = c; + c = gen_fgetc(f); + if(c == EOF) + return -1; + return c + (res<<8); +} + +static int utilReadInt3(FILE* f) +{ + int res = 0; + int c = gen_fgetc(f); + if(c == EOF) + return -1; + res = c; + c = gen_fgetc(f); + if(c == EOF) + return -1; + res = c + (res<<8); + c = gen_fgetc(f); + if(c == EOF) + return -1; + return c + (res<<8); +} + +void utilApplyIPS(const char *ips, u8 **r, int *s) +{ + // from the IPS spec at http://zerosoft.zophar.net/ips.htm + FILE* f = gen_fopen(ips, "rb"); + if(!f) + return; + u8 *rom = *r; + int size = *s; + if(gen_fgetc(f) == 'P' && + gen_fgetc(f) == 'A' && + gen_fgetc(f) == 'T' && + gen_fgetc(f) == 'C' && + gen_fgetc(f) == 'H') + { + int b; + int offset; + int len; + for(;;) + { + // read offset + offset = utilReadInt3(f); + // if offset == EOF, end of patch + if(offset == 0x454f46) + break; + // read length + len = utilReadInt2(f); + if(!len) + { + // len == 0, RLE block + len = utilReadInt2(f); + // byte to fill + int c = gen_fgetc(f); + if(c == -1) + break; + b = (u8)c; + } + else + b= -1; + // check if we need to reallocate our ROM + if((offset + len) >= size) + { + size *= 2; + rom = (u8 *)realloc(rom, size); + *r = rom; + *s = size; + } + if(b == -1) + { + // normal block, just read the data + //if(gen_fread(&rom[offset], 1, len, f) != (size_t)len) + if(gen_fread(&rom[offset], 1, len, f) != (int)len) + break; + } + else + { + // fill the region with the given byte + while(len--) + { + rom[offset++] = b; + } + } + } + } + // close the file + gen_fclose(f); +} + +extern bool cpuIsMultiBoot; + +bool utilIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".elf") == 0) + return true; + if(_stricmp(p, ".mb") == 0) + { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool utilIsGBImage(const char * file) +{ + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".gbc") == 0) + return true; + if(_stricmp(p, ".cgb") == 0) + return true; + if(_stricmp(p, ".sgb") == 0) + return true; + } + } + + return false; +} + +bool utilIsZipFile(const char *file) +{ + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".zip") == 0) + return true; + } + } + + return false; +} + +#if 0 +bool utilIsRarFile(const char *file) +{ + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".rar") == 0) + return true; + } + } + + return false; +} +#endif + +bool utilIsGzipFile(const char *file) +{ + if(strlen(file) > 3) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".gz") == 0) + return true; + if(_stricmp(p, ".z") == 0) + return true; + } + } + + return false; +} + +void utilGetBaseName(const char *file, char *buffer) +{ + strcpy(buffer, file); + + if(utilIsGzipFile(file)) + { + char *p = strrchr(buffer, '.'); + + if(p) + *p = 0; + } +} + +IMAGE_TYPE utilFindType(const char *file) +{ + char buffer[2048]; + + if(utilIsZipFile(file)) + { + unzFile unz = unzOpen(file); + + if(unz == NULL) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), file); + return IMAGE_UNKNOWN; + } + + int r = unzGoToFirstFile(unz); + + if(r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return IMAGE_UNKNOWN; + } + + IMAGE_TYPE found = IMAGE_UNKNOWN; + + unz_file_info info; + + while(true) + { + r = unzGetCurrentFileInfo(unz, + &info, + buffer, + sizeof(buffer), + NULL, + 0, + NULL, + 0); + + if(r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return IMAGE_UNKNOWN; + } + + if(utilIsGBAImage(buffer)) + { + found = IMAGE_GBA; + break; + } + + if(utilIsGBImage(buffer)) + { + found = IMAGE_GB; + break; + } + + r = unzGoToNextFile(unz); + + if(r != UNZ_OK) + break; + } + unzClose(unz); + + if(found == IMAGE_UNKNOWN) + { + systemMessage(MSG_NO_IMAGE_ON_ZIP, + N_("No image found on ZIP file %s"), file); + return found; + } + return found; +#if 0 + } + else if(utilIsRarFile(file)) + { + IMAGE_TYPE found = IMAGE_UNKNOWN; + + ArchiveList_struct *rarList = NULL; + if(urarlib_list((void *)file, (ArchiveList_struct *)&rarList)) + { + ArchiveList_struct *p = rarList; + + while(p) + { + if(utilIsGBAImage(p->item.Name)) + { + found = IMAGE_GBA; + break; + } + + if(utilIsGBImage(p->item.Name)) + { + found = IMAGE_GB; + break; + } + p = p->next; + } + + urarlib_freelist(rarList); + } + return found; +#endif + } + else + { + if(utilIsGzipFile(file)) + utilGetBaseName(file, buffer); + else + strcpy(buffer, file); + + if(utilIsGBAImage(buffer)) + return IMAGE_GBA; + if(utilIsGBImage(buffer)) + return IMAGE_GB; + } + return IMAGE_UNKNOWN; +} + +static int utilGetSize(int size) +{ + int res = 1; + while(res < size) + res <<= 1; + return res; +} + +static u8 *utilLoadFromZip(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + char buffer[2048]; + + unzFile unz = unzOpen(file); + + if(unz == NULL) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), file); + return NULL; + } + int r = unzGoToFirstFile(unz); + + if(r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return NULL; + } + + bool found = false; + + unz_file_info info; + + while(true) + { + r = unzGetCurrentFileInfo(unz, + &info, + buffer, + sizeof(buffer), + NULL, + 0, + NULL, + 0); + + if(r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return NULL; + } + + if(accept(buffer)) + { + found = true; + break; + } + + r = unzGoToNextFile(unz); + + if(r != UNZ_OK) + break; + } + + if(!found) + { + unzClose(unz); + systemMessage(MSG_NO_IMAGE_ON_ZIP, + N_("No image found on ZIP file %s"), file); + return NULL; + } + + int fileSize = info.uncompressed_size; + if(size == 0) + size = fileSize; + r = unzOpenCurrentFile(unz); + + if(r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), buffer); + return NULL; + } + + u8 *image = data; + + if(image == NULL) + { + image = (u8 *)malloc(utilGetSize(size)); + if(image == NULL) + { + unzCloseCurrentFile(unz); + unzClose(unz); + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + return NULL; + } + size = fileSize; + } + int read = fileSize <= size ? fileSize : size; + r = unzReadCurrentFile(unz, + image, + read); + + unzCloseCurrentFile(unz); + unzClose(unz); + + if(r != (int)read) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), buffer); + if(data == NULL) + free(image); + return NULL; + } + + size = fileSize; + + return image; +} + +static u8 *utilLoadGzipFile(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + FILE* f = gen_fopen(file, "rb"); + + if(f == NULL) + { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), file); + return NULL; + } + + gen_fseek(f, -4, SEEK_END); + int fileSize = gen_fgetc(f) | (gen_fgetc(f) << 8) | (gen_fgetc(f) << 16) | (gen_fgetc(f) << 24); + gen_fclose(f); + if(size == 0) + size = fileSize; + + gzFile gz = gzopen(file, "rb"); + + if(gz == NULL) + { + // should not happen, but who knows? + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), file); + return NULL; + } + + u8 *image = data; + + if(image == NULL) + { + image = (u8 *)malloc(utilGetSize(size)); + if(image == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + gen_fclose(f); + return NULL; + } + size = fileSize; + } + int read = fileSize <= size ? fileSize : size; + int r = gzread(gz, image, read); + gzclose(gz); + + if(r != (int)read) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), file); + if(data == NULL) + free(image); + return NULL; + } + + size = fileSize; + + return image; +} + +#if 0 +static u8 *utilLoadRarFile(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + char buffer[2048]; + + ArchiveList_struct *rarList = NULL; + if(urarlib_list((void *)file, (ArchiveList_struct *)&rarList)) + { + ArchiveList_struct *p = rarList; + + bool found = false; + while(p) + { + if(accept(p->item.Name)) + { + strcpy(buffer, p->item.Name); + found = true; + break; + } + p = p->next; + } + if(found) + { + void *memory = NULL; + unsigned long lsize = 0; + size = p->item.UnpSize; + int r = urarlib_get((void *)&memory, &lsize, buffer, (void *)file, ""); + if(!r) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), buffer); + urarlib_freelist(rarList); + return NULL; + } + u8 *image = (u8 *)memory; + if(data != NULL) + { + memcpy(image, data, size); + } + urarlib_freelist(rarList); + return image; + } + systemMessage(MSG_NO_IMAGE_ON_ZIP, + N_("No image found on RAR file %s"), file); + urarlib_freelist(rarList); + return NULL; + } + // nothing found + return NULL; +} +#endif + +u8 *utilLoad(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + if(utilIsZipFile(file)) + { + return utilLoadFromZip(file, accept, data, size); + } + if(utilIsGzipFile(file)) + { + return utilLoadGzipFile(file, accept, data, size); + } +#if 0 + if(utilIsRarFile(file)) + { + return utilLoadRarFile(file, accept, data, size); + } +#endif + + u8 *image = data; + + FILE* f = gen_fopen(file, "rb"); + + if(!f) + { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), file); + return NULL; + } + + gen_fseek(f,0,SEEK_END); + int fileSize = ftell(f); + + gen_fseek(f,0,SEEK_SET); + if(size == 0) + size = fileSize; + + if(image == NULL) + { + image = (u8 *)malloc(utilGetSize(size)); + if(image == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + gen_fclose(f); + return NULL; + } + size = fileSize; + } + int read = fileSize <= size ? fileSize : size; + int r = gen_fread(image, 1, read, f); + gen_fclose(f); + + if(r != (int)read) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), file); + if(data == NULL) + free(image); + return NULL; + } + + size = fileSize; + + return image; +} + +void utilWriteInt(gzFile gzFile, int i) +{ + utilGzWrite(gzFile, &i, sizeof(int)); +} + +int utilReadInt(gzFile gzFile) +{ + int i = 0; + utilGzRead(gzFile, &i, sizeof(int)); + return i; +} + +void utilReadData(gzFile gzFile, variable_desc* data) +{ + while(data->address) + { + utilGzRead(gzFile, data->address, data->size); + data++; + } +} + +void utilWriteData(gzFile gzFile, variable_desc *data) +{ + while(data->address) + { + utilGzWrite(gzFile, data->address, data->size); + data++; + } +} + +gzFile utilGzOpen(const char *file, const char *mode) +{ + utilGzWriteFunc = (int (*)(void *,void * const, unsigned int))gzwrite; + utilGzReadFunc = gzread; + utilGzCloseFunc = gzclose; + + return gzopen(file, mode); +} + +gzFile utilMemGzOpen(char *memory, int available, char *mode) +{ + utilGzWriteFunc = memgzwrite; + utilGzReadFunc = memgzread; + utilGzCloseFunc = memgzclose; + + return memgzopen(memory, available, mode); +} + +int utilGzWrite(gzFile file, const voidp buffer, unsigned int len) +{ + return utilGzWriteFunc(file, buffer, len); +} + +int utilGzRead(gzFile file, voidp buffer, unsigned int len) +{ + return utilGzReadFunc(file, buffer, len); +} + +int utilGzClose(gzFile file) +{ + return utilGzCloseFunc(file); +} + +long utilGzMemTell(gzFile file) +{ + return memtell(file); +} + +void utilGBAFindSave(const u8 *data, const int size) +{ + u32 *p = (u32 *)data; + u32 *end = (u32 *)(data + size); + int saveType = 0; + int flashSize = 0x10000; + bool rtcFound = false; + + while(p < end) + { + u32 d = READ32LE(p); + + if(d == 0x52504545) + { + if(memcmp(p, "EEPROM_", 7) == 0) + { + if(saveType == 0) + saveType = 1; + } + } + else if (d == 0x4D415253) + { + if(memcmp(p, "SRAM_", 5) == 0) + { + if(saveType == 0) + saveType = 2; + } + } + else if (d == 0x53414C46) + { + if(memcmp(p, "FLASH1M_", 8) == 0) + { + if(saveType == 0) + { + saveType = 3; + flashSize = 0x20000; + } + } + else if(memcmp(p, "FLASH", 5) == 0) + { + if(saveType == 0) + { + saveType = 3; + flashSize = 0x10000; + } + } + } + else if (d == 0x52494953) + { + if(memcmp(p, "SIIRTC_V", 8) == 0) + rtcFound = true; + } + p++; + } + // if no matches found, then set it to NONE + if(saveType == 0) + { + saveType = 5; + } + rtcEnable(rtcFound); + cpuSaveType = saveType; + flashSetSize(flashSize); +} diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 0000000..66a0807 --- /dev/null +++ b/src/Util.h @@ -0,0 +1,66 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_UTIL_H +#define VBA_UTIL_H +enum IMAGE_TYPE { + IMAGE_UNKNOWN = -1, + IMAGE_GBA = 0, + IMAGE_GB = 1 +}; + +// save game + +typedef struct + { + void *address; + int size; + } +variable_desc; + +extern bool utilWritePNGFile(const char *, int, int, u8 *); +extern bool utilWriteBMPFile(const char *, int, int, u8 *); +extern void utilApplyIPS(const char *ips, u8 **rom, int *size); +extern void utilWriteBMP(char *, int, int, u8 *); +extern bool utilIsGBAImage(const char *); +extern bool utilIsGBImage(const char *); +extern bool utilIsZipFile(const char *); +extern bool utilIsGzipFile(const char *); +extern bool utilIsRarFile(const char *); +extern void utilGetBaseName(const char *, char *); +extern IMAGE_TYPE utilFindType(const char *); +extern u8 *utilLoad(const char *, + bool (*)(const char*), + u8 *, + int &); + +extern void utilPutDword(u8 *, u32); +extern void utilPutWord(u8 *, u16); +extern void utilWriteData(gzFile, variable_desc *); +extern void utilReadData(gzFile, variable_desc *); +extern int utilReadInt(gzFile); +extern void utilWriteInt(gzFile, int); +extern gzFile utilGzOpen(const char *file, const char *mode); +extern gzFile utilMemGzOpen(char *memory, int available, char *mode); +extern int utilGzWrite(gzFile file, const voidp buffer, unsigned int len); +extern int utilGzRead(gzFile file, voidp buffer, unsigned int len); +extern int utilGzClose(gzFile file); +extern long utilGzMemTell(gzFile file); +extern void utilGBAFindSave(const u8 *, const int); +#endif diff --git a/src/admame.cpp b/src/admame.cpp new file mode 100644 index 0000000..afb8202 --- /dev/null +++ b/src/admame.cpp @@ -0,0 +1,1033 @@ +/* + * This file is part of the Advance project. + * + * Copyright (C) 1999-2002 Andrea Mazzoleni + * + * 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 2 of the License, or + * (at your option) any later version. + * + * 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 program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * In addition, as a special exception, Andrea Mazzoleni + * gives permission to link the code of this program with + * the MAME library (or with modified versions of MAME that use the + * same license as MAME), and distribute linked combinations including + * the two. You must obey the GNU General Public License in all + * respects for all of the code used other than MAME. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +/* + * Alternatively at the previous license terms, you are allowed to use this + * code in your program with these conditions: + * - the program is not used in commercial activities. + * - the whole source code of the program is released with the binary. + */ + +#include "System.h" + +#ifdef MMX +extern "C" bool cpu_mmx; +#endif + +static void internal_scale2x_16_def(u16 *dst, const u16* src0, const u16* src1, const u16* src2, unsigned count) +{ + /* first pixel */ + dst[0] = src1[0]; + if (src1[1] == src0[0] && src2[0] != src0[0]) + dst[1] = src0[0]; + else + dst[1] = src1[0]; + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) + { + if (src0[0] != src2[0] && src1[-1] != src1[1]) + { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } + else + { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src1[-1] == src0[0] && src2[0] != src0[0]) + dst[0] = src0[0]; + else + dst[0] = src1[0]; + dst[1] = src1[0]; +} + +static void internal_scale2x_32_def(u32* dst, + const u32* src0, + const u32* src1, + const u32* src2, + unsigned count) +{ + /* first pixel */ + dst[0] = src1[0]; + if (src1[1] == src0[0] && src2[0] != src0[0]) + dst[1] = src0[0]; + else + dst[1] = src1[0]; + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) + { + if (src0[0] != src2[0] && src1[-1] != src1[1]) + { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } + else + { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src1[-1] == src0[0] && src2[0] != src0[0]) + dst[0] = src0[0]; + else + dst[0] = src1[0]; + dst[1] = src1[0]; +} + +#ifdef MMX +static void internal_scale2x_16_mmx_single(u16* dst, const u16* src0, const u16* src1, const u16* src2, unsigned count) +{ + /* always do the first and last run */ + count -= 2*4; + +#ifdef __GNUC__ + __asm__ __volatile__( + /* first run */ + /* set the current, current_pre, current_next registers */ + "movq 0(%1), %%mm0\n" + "movq 0(%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psllq $48,%%mm0\n" + "psllq $48,%%mm1\n" + "psrlq $48, %%mm0\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $16,%%mm2\n" + "psrlq $16,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqw %%mm6,%%mm2\n" + "pcmpeqw %%mm6,%%mm4\n" + "pcmpeqw (%2),%%mm3\n" + "pcmpeqw (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqw %%mm1,%%mm2\n" + "pcmpeqw %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpcklwd %%mm4,%%mm2\n" + "punpckhwd %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + /* central runs */ + "shrl $2,%4\n" + "jz 1f\n" + + "0:\n" + + /* set the current, current_pre, current_next registers */ + "movq -8(%1),%%mm0\n" + "movq (%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psrlq $48,%%mm0\n" + "psllq $48,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $16,%%mm2\n" + "psrlq $16,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqw %%mm6,%%mm2\n" + "pcmpeqw %%mm6,%%mm4\n" + "pcmpeqw (%2),%%mm3\n" + "pcmpeqw (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqw %%mm1,%%mm2\n" + "pcmpeqw %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpcklwd %%mm4,%%mm2\n" + "punpckhwd %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + "decl %4\n" + "jnz 0b\n" + "1:\n" + + /* final run */ + /* set the current, current_pre, current_next registers */ + "movq (%1),%%mm1\n" + "movq (%1),%%mm7\n" + "movq -8(%1),%%mm0\n" + "psrlq $48,%%mm1\n" + "psrlq $48,%%mm0\n" + "psllq $48,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $16,%%mm2\n" + "psrlq $16,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqw %%mm6,%%mm2\n" + "pcmpeqw %%mm6,%%mm4\n" + "pcmpeqw (%2),%%mm3\n" + "pcmpeqw (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqw %%mm1,%%mm2\n" + "pcmpeqw %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpcklwd %%mm4,%%mm2\n" + "punpckhwd %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + "emms\n" + + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count) + : + : "cc" + ); +#else + __asm { + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, dst; + mov esi, count; + + /* first run */ + /* set the current, current_pre, current_next registers */ + movq mm0, qword ptr [ebx]; + movq mm7, qword ptr [ebx]; + movq mm1, qword ptr [ebx + 8]; + psllq mm0, 48; + psllq mm1, 48; + psrlq mm0, 48; + movq mm2, mm7; + movq mm3, mm7; + psllq mm2, 16; + psrlq mm3, 16; + por mm0, mm2; + por mm1, mm3; + + /* current_upper */ + movq mm6, qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2, mm0; + movq mm4, mm1; + movq mm3, mm0; + movq mm5, mm1; + pcmpeqw mm2, mm6; + pcmpeqw mm4, mm6; + pcmpeqw mm3, qword ptr [ecx]; + pcmpeqw mm5, qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqw mm2,mm1; + pcmpeqw mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst0 */ + movq mm3,mm2; + punpcklwd mm2,mm4; + punpckhwd mm3,mm4; + movq qword ptr [edx], mm2; + movq qword ptr [edx + 8], mm3; + + /* next */ + add eax, 8; + add ebx, 8; + add ecx, 8; + add edx, 16; + + /* central runs */ + shr esi, 2; + jz label1; + align 4; + label0: + + /* set the current, current_pre, current_next registers */ + movq mm0, qword ptr [ebx-8]; + movq mm7, qword ptr [ebx]; + movq mm1, qword ptr [ebx+8]; + psrlq mm0,48; + psllq mm1,48; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,16; + psrlq mm3,16; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6, qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqw mm2,mm6; + pcmpeqw mm4,mm6; + pcmpeqw mm3, qword ptr [ecx]; + pcmpeqw mm5, qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqw mm2,mm1; + pcmpeqw mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpcklwd mm2,mm4; + punpckhwd mm3,mm4; + movq qword ptr [edx], mm2; + movq qword ptr [edx+8], mm3; + + /* next */ + add eax,8; + add ebx,8; + add ecx,8; + add edx,16; + + dec esi; + jnz label0; + label1: + + /* final run */ + /* set the current, current_pre, current_next registers */ + movq mm1, qword ptr [ebx]; + movq mm7, qword ptr [ebx]; + movq mm0, qword ptr [ebx-8]; + psrlq mm1,48; + psrlq mm0,48; + psllq mm1,48; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,16; + psrlq mm3,16; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6, qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqw mm2,mm6; + pcmpeqw mm4,mm6; + pcmpeqw mm3, qword ptr [ecx]; + pcmpeqw mm5, qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqw mm2,mm1; + pcmpeqw mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpcklwd mm2,mm4; + punpckhwd mm3,mm4; + movq qword ptr [edx], mm2; + movq qword ptr [edx+8], mm3; + + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov dst, edx; + mov count, esi; + + emms; + } +#endif +} + +static void internal_scale2x_32_mmx_single(u32* dst, const u32* src0, const u32* src1, const u32* src2, unsigned count) +{ + /* always do the first and last run */ + count -= 2*2; + +#ifdef __GNUC__ + __asm__ __volatile__( + /* first run */ + /* set the current, current_pre, current_next registers */ + "movq 0(%1),%%mm0\n" + "movq 0(%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psllq $32,%%mm0\n" + "psllq $32,%%mm1\n" + "psrlq $32,%%mm0\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $32,%%mm2\n" + "psrlq $32,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqd %%mm6,%%mm2\n" + "pcmpeqd %%mm6,%%mm4\n" + "pcmpeqd (%2),%%mm3\n" + "pcmpeqd (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqd %%mm1,%%mm2\n" + "pcmpeqd %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpckldq %%mm4,%%mm2\n" + "punpckhdq %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + /* central runs */ + "shrl $1,%4\n" + "jz 1f\n" + + "0:\n" + + /* set the current, current_pre, current_next registers */ + "movq -8(%1),%%mm0\n" + "movq (%1),%%mm7\n" + "movq 8(%1),%%mm1\n" + "psrlq $32,%%mm0\n" + "psllq $32,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $32,%%mm2\n" + "psrlq $32,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqd %%mm6,%%mm2\n" + "pcmpeqd %%mm6,%%mm4\n" + "pcmpeqd (%2),%%mm3\n" + "pcmpeqd (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqd %%mm1,%%mm2\n" + "pcmpeqd %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpckldq %%mm4,%%mm2\n" + "punpckhdq %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + + /* next */ + "addl $8,%0\n" + "addl $8,%1\n" + "addl $8,%2\n" + "addl $16,%3\n" + + "decl %4\n" + "jnz 0b\n" + "1:\n" + + /* final run */ + /* set the current, current_pre, current_next registers */ + "movq (%1),%%mm1\n" + "movq (%1),%%mm7\n" + "movq -8(%1), %%mm0\n" + "psrlq $32,%%mm1\n" + "psrlq $32,%%mm0\n" + "psllq $32,%%mm1\n" + "movq %%mm7,%%mm2\n" + "movq %%mm7,%%mm3\n" + "psllq $32,%%mm2\n" + "psrlq $32,%%mm3\n" + "por %%mm2,%%mm0\n" + "por %%mm3,%%mm1\n" + + /* current_upper */ + "movq (%0),%%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "movq %%mm0,%%mm3\n" + "movq %%mm1,%%mm5\n" + "pcmpeqd %%mm6,%%mm2\n" + "pcmpeqd %%mm6,%%mm4\n" + "pcmpeqd (%2),%%mm3\n" + "pcmpeqd (%2),%%mm5\n" + "pandn %%mm2,%%mm3\n" + "pandn %%mm4,%%mm5\n" + "movq %%mm0,%%mm2\n" + "movq %%mm1,%%mm4\n" + "pcmpeqd %%mm1,%%mm2\n" + "pcmpeqd %%mm0,%%mm4\n" + "pandn %%mm3,%%mm2\n" + "pandn %%mm5,%%mm4\n" + "movq %%mm2,%%mm3\n" + "movq %%mm4,%%mm5\n" + "pand %%mm6,%%mm2\n" + "pand %%mm6,%%mm4\n" + "pandn %%mm7,%%mm3\n" + "pandn %%mm7,%%mm5\n" + "por %%mm3,%%mm2\n" + "por %%mm5,%%mm4\n" + + /* set *dst */ + "movq %%mm2,%%mm3\n" + "punpckldq %%mm4,%%mm2\n" + "punpckhdq %%mm4,%%mm3\n" + "movq %%mm2,(%3)\n" + "movq %%mm3,8(%3)\n" + "emms\n" + + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count) + : + : "cc" + ); +#else + __asm { + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, dst; + mov esi, count; + + /* first run */ + /* set the current, current_pre, current_next registers */ + movq mm0,qword ptr [ebx]; + movq mm7,qword ptr [ebx]; + movq mm1,qword ptr [ebx + 8]; + psllq mm0,32; + psllq mm1,32; + psrlq mm0,32; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,32; + psrlq mm3,32; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6,qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqd mm2,mm6; + pcmpeqd mm4,mm6; + pcmpeqd mm3,qword ptr [ecx]; + pcmpeqd mm5,qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqd mm2,mm1; + pcmpeqd mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpckldq mm2,mm4; + punpckhdq mm3,mm4; + movq qword ptr [edx],mm2; + movq qword ptr [edx+8],mm3; + + /* next */ + add eax,8; + add ebx,8; + add ecx,8; + add edx,16; + + /* central runs */ + shr esi,1; + jz label1; + label0: + + /* set the current, current_pre, current_next registers */ + movq mm0,qword ptr [ebx-8]; + movq mm7,qword ptr [ebx]; + movq mm1,qword ptr [ebx+8]; + psrlq mm0,32; + psllq mm1,32; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,32; + psrlq mm3,32; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6,qword ptr[eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqd mm2,mm6; + pcmpeqd mm4,mm6; + pcmpeqd mm3,qword ptr[ecx]; + pcmpeqd mm5,qword ptr[ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqd mm2,mm1; + pcmpeqd mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpckldq mm2,mm4; + punpckhdq mm3,mm4; + movq qword ptr [edx],mm2; + movq qword ptr [edx+8],mm3; + + /* next */ + add eax,8; + add ebx,8; + add ecx,8; + add edx,16; + + dec esi; + jnz label0; + label1: + + /* final run */ + /* set the current, current_pre, current_next registers */ + movq mm1,qword ptr [ebx]; + movq mm7,qword ptr [ebx]; + movq mm0,qword ptr [ebx-8]; + psrlq mm1,32; + psrlq mm0,32; + psllq mm1,32; + movq mm2,mm7; + movq mm3,mm7; + psllq mm2,32; + psrlq mm3,32; + por mm0,mm2; + por mm1,mm3; + + /* current_upper */ + movq mm6,qword ptr [eax]; + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + movq mm2,mm0; + movq mm4,mm1; + movq mm3,mm0; + movq mm5,mm1; + pcmpeqd mm2,mm6; + pcmpeqd mm4,mm6; + pcmpeqd mm3,qword ptr [ecx]; + pcmpeqd mm5,qword ptr [ecx]; + pandn mm3,mm2; + pandn mm5,mm4; + movq mm2,mm0; + movq mm4,mm1; + pcmpeqd mm2,mm1; + pcmpeqd mm4,mm0; + pandn mm2,mm3; + pandn mm4,mm5; + movq mm3,mm2; + movq mm5,mm4; + pand mm2,mm6; + pand mm4,mm6; + pandn mm3,mm7; + pandn mm5,mm7; + por mm2,mm3; + por mm4,mm5; + + /* set *dst */ + movq mm3,mm2; + punpckldq mm2,mm4; + punpckhdq mm3,mm4; + movq qword ptr [edx],mm2; + movq qword ptr [edx+8],mm3; + + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov dst, edx; + mov count, esi; + + emms; + } +#endif +} + +static void internal_scale2x_16_mmx(u16* dst0, u16* dst1, const u16* src0, const u16* src1, const u16* src2, unsigned count) +{ + // assert( count >= 2*4 ); + internal_scale2x_16_mmx_single(dst0, src0, src1, src2, count); + internal_scale2x_16_mmx_single(dst1, src2, src1, src0, count); +} + +static void internal_scale2x_32_mmx(u32* dst0, u32* dst1, const u32* src0, const u32* src1, const u32* src2, unsigned count) +{ + // assert( count >= 2*2 ); + internal_scale2x_32_mmx_single(dst0, src0, src1, src2, count); + internal_scale2x_32_mmx_single(dst1, src2, src1, src0, count); +} +#endif + +void AdMame2x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u16 *dst0 = (u16 *)dstPtr; + u16 *dst1 = dst0 + (dstPitch >> 1); + + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = src0 + (srcPitch >> 1); + u16 *src2 = src1 + (srcPitch >> 1); +#ifdef MMX + if(cpu_mmx) + { + internal_scale2x_16_mmx(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) + { + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_mmx(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 1; + --count; + } + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_mmx(dst0, dst1, src0, src1, src1, width); + } + else + { +#endif + internal_scale2x_16_def(dst0, src0, src0, src1, width); + internal_scale2x_16_def(dst1, src1, src0, src0, width); + + int count = height; + + count -= 2; + while(count) + { + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_def(dst0, src0, src1, src2, width); + internal_scale2x_16_def(dst1, src2, src1, src0, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 1; + --count; + } + dst0 += dstPitch; + dst1 += dstPitch; + internal_scale2x_16_def(dst0, src0, src1, src1, width); + internal_scale2x_16_def(dst1, src1, src1, src0, width); +#ifdef MMX + } +#endif +} + +void AdMame2x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *dst0 = (u32 *)dstPtr; + u32 *dst1 = dst0 + (dstPitch >> 2); + + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = src0 + (srcPitch >> 2); + u32 *src2 = src1 + (srcPitch >> 2); +#ifdef MMX + if(cpu_mmx) + { + internal_scale2x_32_mmx(dst0, dst1, src0, src0, src1, width); + + int count = height; + + count -= 2; + while(count) + { + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_mmx(dst0, dst1, src0, src1, src2, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 2; + --count; + } + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_mmx(dst0, dst1, src0, src1, src1, width); + } + else + { +#endif + internal_scale2x_32_def(dst0, src0, src0, src1, width); + internal_scale2x_32_def(dst1, src1, src0, src0, width); + + int count = height; + + count -= 2; + while(count) + { + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_def(dst0, src0, src1, src2, width); + internal_scale2x_32_def(dst1, src2, src1, src0, width); + src0 = src1; + src1 = src2; + src2 += srcPitch >> 2; + --count; + } + dst0 += dstPitch >> 1; + dst1 += dstPitch >> 1; + internal_scale2x_32_def(dst0, src0, src1, src1, width); + internal_scale2x_32_def(dst1, src1, src1, src0, width); +#ifdef MMX + } +#endif +} diff --git a/src/agbprint.cpp b/src/agbprint.cpp new file mode 100644 index 0000000..f1db198 --- /dev/null +++ b/src/agbprint.cpp @@ -0,0 +1,106 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "GBA.h" +#include "Globals.h" +#include "Port.h" + +#define debuggerWriteHalfWord(addr, value) \ + WRITE16LE((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask], (value)) + +#define debuggerReadHalfWord(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +static bool agbPrintEnabled = false; +static bool agbPrintProtect = false; + +bool agbPrintWrite(u32 address, u16 value) +{ + if(agbPrintEnabled) + { + if(address == 0x9fe2ffe) + { // protect + agbPrintProtect = (value != 0); + debuggerWriteHalfWord(address, value); + return true; + } + else + { + if(agbPrintProtect && + ((address >= 0x9fe20f8 && address <= 0x9fe20ff) // control structure + || (address >= 0x8fd0000 && address <= 0x8fdffff) + || (address >= 0x9fd0000 && address <= 0x9fdffff))) + { + debuggerWriteHalfWord(address, value); + return true; + } + } + } + return false; +} + +void agbPrintReset() +{ + agbPrintProtect = false; +} + +void agbPrintEnable(bool enable) +{ + agbPrintEnabled = enable; +} + +bool agbPrintIsEnabled() +{ + return agbPrintEnabled; +} + +extern void (*dbgOutput)(char *, u32); + +void agbPrintFlush() +{ + u16 get = debuggerReadHalfWord(0x9fe20fc); + u16 put = debuggerReadHalfWord(0x9fe20fe); + + u32 address = (debuggerReadHalfWord(0x9fe20fa) << 16); + if(address != 0xfd0000 && address != 0x1fd0000) + { + dbgOutput("Did you forget to call AGBPrintInit?\n", 0); + // get rid of the text otherwise we will continue to be called + debuggerWriteHalfWord(0x9fe20fc, put); + return; + } + + u8 *data = &rom[address]; + + while(get != put) + { + char c = data[get++]; + char s[2]; + s[0] = c; + s[1] = 0; + + if(systemVerbose & VERBOSE_AGBPRINT) + dbgOutput(s, 0); + if(c == '\n') + break; + } + debuggerWriteHalfWord(0x9fe20fc, get); +} diff --git a/src/agbprint.h b/src/agbprint.h new file mode 100644 index 0000000..d7742de --- /dev/null +++ b/src/agbprint.h @@ -0,0 +1,27 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_AGBPRINT_H +#define VBA_AGBPRINT_H +extern void agbPrintEnable(bool); +extern bool agbPrintIsEnabled(); +extern void agbPrintReset(); +extern bool agbPrintWrite(u32, u16); +extern void agbPrintFlush(); +#endif diff --git a/src/arm-new.h b/src/arm-new.h new file mode 100644 index 0000000..c8e769b --- /dev/null +++ b/src/arm-new.h @@ -0,0 +1,7472 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifdef BKPT_SUPPORT +#define CONSOLE_OUTPUT(a,b) \ + extern void (*dbgOutput)(char *, u32);\ + if((opcode == 0xe0000000) && (reg[0].I == 0xC0DED00D)) {\ + dbgOutput((a), (b));\ + } +#else +#define CONSOLE_OUTPUT(a,b) +#endif + +#define OP_AND \ + reg[dest].I = reg[(opcode>>16)&15].I & value;\ + CONSOLE_OUTPUT(NULL,reg[2].I); + +#define OP_ANDS \ + reg[dest].I = reg[(opcode>>16)&15].I & value;\ + \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = (reg[dest].I) ? false : true;\ + C_FLAG = C_OUT; + +#define OP_EOR \ + reg[dest].I = reg[(opcode>>16)&15].I ^ value; + +#define OP_EORS \ + reg[dest].I = reg[(opcode>>16)&15].I ^ value;\ + \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = (reg[dest].I) ? false : true;\ + C_FLAG = C_OUT; +#ifdef C_CORE +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) +#define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) |\ + (NEG(a) & POS(c)) |\ + (NEG(b) & POS(c))) ? true : false; +#define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) |\ + (NEG(a) & POS(c)) |\ + (POS(b) & POS(c))) ? true : false; +#define SUBOVERFLOW(a, b, c)\ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#define OP_SUB \ + {\ + reg[dest].I = reg[base].I - value;\ + } +#define OP_SUBS \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define OP_RSB \ + {\ + reg[dest].I = value - reg[base].I;\ + } +#define OP_RSBS \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = rhs - lhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(rhs, lhs, res);\ + SUBOVERFLOW(rhs, lhs, res);\ + } +#define OP_ADD \ + {\ + reg[dest].I = reg[base].I + value;\ + } +#define OP_ADDS \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#define OP_ADC \ + {\ + reg[dest].I = reg[base].I + value + (u32)C_FLAG;\ + } +#define OP_ADCS \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs + (u32)C_FLAG;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#define OP_SBC \ + {\ + reg[dest].I = reg[base].I - value - !((u32)C_FLAG);\ + } +#define OP_SBCS \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs - !((u32)C_FLAG);\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define OP_RSC \ + {\ + reg[dest].I = value - reg[base].I - !((u32)C_FLAG);\ + } +#define OP_RSCS \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = rhs - lhs - !((u32)C_FLAG);\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(rhs, lhs, res);\ + SUBOVERFLOW(rhs, lhs, res);\ + } +#define OP_CMP \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define OP_CMN \ + {\ + u32 lhs = reg[base].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } + +#define LOGICAL_LSL_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = (v >> (32 - shift)) & 1 ? true : false;\ + value = v << shift;\ + } +#define LOGICAL_LSR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = v >> shift;\ + } +#define LOGICAL_ASR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = ((s32)v >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)v >> (int)shift;\ + } +#define LOGICAL_ROR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define LOGICAL_RRX_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + shift = (int)C_FLAG;\ + C_OUT = (v & 1) ? true : false;\ + value = ((v >> 1) |\ + (shift << 31));\ + } +#define LOGICAL_ROR_IMM \ + {\ + u32 v = opcode & 0xff;\ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ARITHMETIC_LSL_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = v << shift;\ + } +#define ARITHMETIC_LSR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = v >> shift;\ + } +#define ARITHMETIC_ASR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = (s32)v >> (int)shift;\ + } +#define ARITHMETIC_ROR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ARITHMETIC_RRX_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + shift = (int)C_FLAG;\ + value = ((v >> 1) |\ + (shift << 31));\ + } +#define ARITHMETIC_ROR_IMM \ + {\ + u32 v = opcode & 0xff;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ROR_IMM_MSR \ + {\ + u32 v = opcode & 0xff;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ROR_VALUE \ + {\ + value = ((value << (32 - shift)) |\ + (value >> shift));\ + } +#define RCR_VALUE \ + {\ + shift = (int)C_FLAG;\ + value = ((value >> 1) |\ + (shift << 31));\ + } +#else +#ifdef __GNUC__ +#ifdef __POWERPC__ +#define OP_SUB \ + {\ + reg[dest].I = reg[base].I - value;\ + } +#define OP_SUBS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#define OP_RSB \ + {\ + reg[dest].I = value - reg[base].I;\ + } +#define OP_RSBS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#define OP_ADD \ + {\ + reg[dest].I = reg[base].I + value;\ + } + +#define OP_ADDS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#define OP_ADC \ + {\ + reg[dest].I = reg[base].I + value + (u32)C_FLAG;\ + } +#define OP_ADCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#define OP_SBC \ + {\ + reg[dest].I = reg[base].I - value - (C_FLAG^1);\ + } +#define OP_SBCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#define OP_RSC \ + {\ + reg[dest].I = value - reg[base].I - (C_FLAG^1);\ + } +#define OP_RSCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#define OP_CMP \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#define OP_CMN \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + +#define LOGICAL_LSL_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = (v >> (32 - shift)) & 1 ? true : false;\ + value = v << shift;\ + } +#define LOGICAL_LSR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = v >> shift;\ + } +#define LOGICAL_ASR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = ((s32)v >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)v >> (int)shift;\ + } +#define LOGICAL_ROR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define LOGICAL_RRX_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + shift = (int)C_FLAG;\ + C_OUT = (v & 1) ? true : false;\ + value = ((v >> 1) |\ + (shift << 31));\ + } +#define LOGICAL_ROR_IMM \ + {\ + u32 v = opcode & 0xff;\ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ARITHMETIC_LSL_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = v << shift;\ + } +#define ARITHMETIC_LSR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = v >> shift;\ + } +#define ARITHMETIC_ASR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = (s32)v >> (int)shift;\ + } +#define ARITHMETIC_ROR_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ARITHMETIC_RRX_REG \ + {\ + u32 v = reg[opcode & 0x0f].I;\ + shift = (int)C_FLAG;\ + value = ((v >> 1) |\ + (shift << 31));\ + } +#define ARITHMETIC_ROR_IMM \ + {\ + u32 v = opcode & 0xff;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ROR_IMM_MSR \ + {\ + u32 v = opcode & 0xff;\ + value = ((v << (32 - shift)) |\ + (v >> shift));\ + } +#define ROR_VALUE \ + {\ + value = ((value << (32 - shift)) |\ + (value >> shift));\ + } +#define RCR_VALUE \ + {\ + shift = (int)C_FLAG;\ + value = ((value >> 1) |\ + (shift << 31));\ + } +#else +#define OP_SUB \ + asm ("sub %1, %%ebx;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); + +#define OP_SUBS \ + asm ("sub %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); + +#define OP_RSB \ + asm ("sub %1, %%ebx;"\ + : "=b" (reg[dest].I)\ + : "r" (reg[base].I), "b" (value)); + +#define OP_RSBS \ + asm ("sub %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (reg[base].I), "b" (value)); + +#define OP_ADD \ + asm ("add %1, %%ebx;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); + +#define OP_ADDS \ + asm ("add %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); + +#define OP_ADC \ + asm ("bt $0, C_FLAG;"\ + "adc %1, %%ebx;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); + +#define OP_ADCS \ + asm ("bt $0, C_FLAG;"\ + "adc %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); + +#define OP_SBC \ + asm ("bt $0, C_FLAG;"\ + "cmc;"\ + "sbb %1, %%ebx;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); + +#define OP_SBCS \ + asm ("bt $0, C_FLAG;"\ + "cmc;"\ + "sbb %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[base].I)); +#define OP_RSC \ + asm ("bt $0, C_FLAG;"\ + "cmc;"\ + "sbb %1, %%ebx;"\ + : "=b" (reg[dest].I)\ + : "r" (reg[base].I), "b" (value)); + +#define OP_RSCS \ + asm ("bt $0, C_FLAG;"\ + "cmc;"\ + "sbb %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (reg[base].I), "b" (value)); +#define OP_CMP \ + asm ("sub %0, %1;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + :\ + : "r" (value), "r" (reg[base].I)); + +#define OP_CMN \ + asm ("add %0, %1;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : \ + : "r" (value), "r" (reg[base].I)); +#define LOGICAL_LSL_REG \ + asm("shl %%cl, %%eax;"\ + "setcb %%cl;"\ + : "=a" (value), "=c" (C_OUT)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_LSR_REG \ + asm("shr %%cl, %%eax;"\ + "setcb %%cl;"\ + : "=a" (value), "=c" (C_OUT)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_ASR_REG \ + asm("sar %%cl, %%eax;"\ + "setcb %%cl;"\ + : "=a" (value), "=c" (C_OUT)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_ROR_REG \ + asm("ror %%cl, %%eax;"\ + "setcb %%cl;"\ + : "=a" (value), "=c" (C_OUT)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_RRX_REG \ + asm("bt $0, C_FLAG;"\ + "rcr $1, %%eax;"\ + "setcb %%cl;"\ + : "=a" (value), "=c" (C_OUT)\ + : "a" (reg[opcode & 0x0f].I)); + +#define LOGICAL_ROR_IMM \ + asm("ror %%cl, %%eax;"\ + "setcb %%cl;"\ + : "=a" (value), "=c" (C_OUT)\ + : "a" (opcode & 0xff), "c" (shift)); +#define ARITHMETIC_LSL_REG \ + asm("\ + shl %%cl, %%eax;"\ + : "=a" (value)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_LSR_REG \ + asm("\ + shr %%cl, %%eax;"\ + : "=a" (value)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_ASR_REG \ + asm("\ + sar %%cl, %%eax;"\ + : "=a" (value)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_ROR_REG \ + asm("\ + ror %%cl, %%eax;"\ + : "=a" (value)\ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_RRX_REG \ + asm("\ + bt $0, C_FLAG;\ + rcr $1, %%eax;"\ + : "=a" (value)\ + : "a" (reg[opcode & 0x0f].I)); + +#define ARITHMETIC_ROR_IMM \ + asm("\ + ror %%cl, %%eax;"\ + : "=a" (value)\ + : "a" (opcode & 0xff), "c" (shift)); +#define ROR_IMM_MSR \ + asm ("ror %%cl, %%eax;"\ + : "=a" (value)\ + : "a" (opcode & 0xFF), "c" (shift)); +#define ROR_VALUE \ + asm("ror %%cl, %0"\ + : "=r" (value)\ + : "r" (value), "c" (shift)); +#define RCR_VALUE \ + asm("bt $0, C_FLAG;"\ + "rcr $1, %0"\ + : "=r" (value)\ + : "r" (value)); +#endif +#else +#define OP_SUB \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm sub ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + } + +#define OP_SUBS \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm sub ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + +#define OP_RSB \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm mov eax, value\ + __asm sub eax, ebx\ + __asm mov ebx, dest\ + __asm mov dword ptr [OFFSET reg+4*ebx], eax\ + } + +#define OP_RSBS \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm mov eax, value\ + __asm sub eax, ebx\ + __asm mov ebx, dest\ + __asm mov dword ptr [OFFSET reg+4*ebx], eax\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + +#define OP_ADD \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm add ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + } + +#define OP_ADDS \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm add ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + +#define OP_ADC \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm bt word ptr C_FLAG, 0\ + __asm adc ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + } + +#define OP_ADCS \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm bt word ptr C_FLAG, 0\ + __asm adc ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + +#define OP_SBC \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg + 4*ebx]\ + __asm mov eax, value\ + __asm bt word ptr C_FLAG, 0\ + __asm cmc\ + __asm sbb ebx, eax\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg + 4*eax], ebx\ + } + +#define OP_SBCS \ + {\ + __asm mov ebx, base\ + __asm mov ebx, dword ptr [OFFSET reg + 4*ebx]\ + __asm mov eax, value\ + __asm bt word ptr C_FLAG, 0\ + __asm cmc\ + __asm sbb ebx, eax\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg + 4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define OP_RSC \ + {\ + __asm mov ebx, value\ + __asm mov eax, base\ + __asm mov eax, dword ptr[OFFSET reg + 4*eax]\ + __asm bt word ptr C_FLAG, 0\ + __asm cmc\ + __asm sbb ebx, eax\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg + 4*eax], ebx\ + } + +#define OP_RSCS \ + {\ + __asm mov ebx, value\ + __asm mov eax, base\ + __asm mov eax, dword ptr[OFFSET reg + 4*eax]\ + __asm bt word ptr C_FLAG, 0\ + __asm cmc\ + __asm sbb ebx, eax\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg + 4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define OP_CMP \ + {\ + __asm mov eax, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + +#define OP_CMN \ + {\ + __asm mov eax, base\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define LOGICAL_LSL_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0f\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shl eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_OUT + +#define LOGICAL_LSR_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0f\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shr eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_OUT + +#define LOGICAL_ASR_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0f\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm sar eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_OUT + +#define LOGICAL_ROR_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0F\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr shift\ + __asm ror eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_OUT + +#define LOGICAL_RRX_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0F\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm bt word ptr C_OUT, 0\ + __asm rcr eax, 1\ + __asm mov value, eax\ + __asm setc byte ptr C_OUT + +#define LOGICAL_ROR_IMM \ + __asm mov eax, opcode\ + __asm and eax, 0xff\ + __asm mov cl, byte ptr shift\ + __asm ror eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_OUT +#define ARITHMETIC_LSL_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0f\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shl eax, cl\ + __asm mov value, eax + +#define ARITHMETIC_LSR_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0f\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shr eax, cl\ + __asm mov value, eax + +#define ARITHMETIC_ASR_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0f\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm sar eax, cl\ + __asm mov value, eax + +#define ARITHMETIC_ROR_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0F\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr shift\ + __asm ror eax, cl\ + __asm mov value, eax + +#define ARITHMETIC_RRX_REG \ + __asm mov eax, opcode\ + __asm and eax, 0x0F\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm bt word ptr C_FLAG, 0\ + __asm rcr eax, 1\ + __asm mov value, eax + +#define ARITHMETIC_ROR_IMM \ + __asm mov eax, opcode\ + __asm and eax, 0xff\ + __asm mov cl, byte ptr shift\ + __asm ror eax, cl\ + __asm mov value, eax +#define ROR_IMM_MSR \ + {\ + __asm mov eax, opcode\ + __asm and eax, 0xff\ + __asm mov cl, byte ptr shift\ + __asm ror eax, CL\ + __asm mov value, eax\ + } +#define ROR_VALUE \ + {\ + __asm mov cl, byte ptr shift\ + __asm ror dword ptr value, cl\ + } +#define RCR_VALUE \ + {\ + __asm mov cl, byte ptr shift\ + __asm bt word ptr C_FLAG, 0\ + __asm rcr dword ptr value, 1\ + } +#endif +#endif + +#define OP_TST \ + u32 res = reg[base].I & value;\ + N_FLAG = (res & 0x80000000) ? true : false;\ + Z_FLAG = (res) ? false : true;\ + C_FLAG = C_OUT; + +#define OP_TEQ \ + u32 res = reg[base].I ^ value;\ + N_FLAG = (res & 0x80000000) ? true : false;\ + Z_FLAG = (res) ? false : true;\ + C_FLAG = C_OUT; + +#define OP_ORR \ + reg[dest].I = reg[base].I | value; + +#define OP_ORRS \ + reg[dest].I = reg[base].I | value;\ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = (reg[dest].I) ? false : true;\ + C_FLAG = C_OUT; + +#define OP_MOV \ + reg[dest].I = value; + +#define OP_MOVS \ + reg[dest].I = value;\ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = (reg[dest].I) ? false : true;\ + C_FLAG = C_OUT; + +#define OP_BIC \ + reg[dest].I = reg[base].I & (~value); + +#define OP_BICS \ + reg[dest].I = reg[base].I & (~value);\ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = (reg[dest].I) ? false : true;\ + C_FLAG = C_OUT; + +#define OP_MVN \ + reg[dest].I = ~value; + +#define OP_MVNS \ + reg[dest].I = ~value; \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = (reg[dest].I) ? false : true;\ + C_FLAG = C_OUT; + +#define CASE_16(BASE) \ + case BASE:\ + case BASE+1:\ + case BASE+2:\ + case BASE+3:\ + case BASE+4:\ + case BASE+5:\ + case BASE+6:\ + case BASE+7:\ + case BASE+8:\ + case BASE+9:\ + case BASE+10:\ + case BASE+11:\ + case BASE+12:\ + case BASE+13:\ + case BASE+14:\ + case BASE+15: + +#define CASE_256(BASE) \ + CASE_16(BASE)\ + CASE_16(BASE+0x10)\ + CASE_16(BASE+0x20)\ + CASE_16(BASE+0x30)\ + CASE_16(BASE+0x40)\ + CASE_16(BASE+0x50)\ + CASE_16(BASE+0x60)\ + CASE_16(BASE+0x70)\ + CASE_16(BASE+0x80)\ + CASE_16(BASE+0x90)\ + CASE_16(BASE+0xa0)\ + CASE_16(BASE+0xb0)\ + CASE_16(BASE+0xc0)\ + CASE_16(BASE+0xd0)\ + CASE_16(BASE+0xe0)\ + CASE_16(BASE+0xf0) + +#define LOGICAL_DATA_OPCODE(OPCODE, OPCODE2, BASE) \ + case BASE: \ + case BASE+8:\ + {\ + /* OP Rd,Rb,Rm LSL # */ \ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + \ + if(shift) {\ + LOGICAL_LSL_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+2:\ + case BASE+10:\ + {\ + /* OP Rd,Rb,Rm LSR # */ \ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_LSR_REG\ + } else {\ + value = 0;\ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false;\ + }\ + \ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+4:\ + case BASE+12:\ + {\ + /* OP Rd,Rb,Rm ASR # */\ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_ASR_REG\ + } else {\ + if(reg[opcode & 0x0F].I & 0x80000000){\ + value = 0xFFFFFFFF;\ + C_OUT = true;\ + } else {\ + value = 0;\ + C_OUT = false;\ + } \ + }\ + \ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+6:\ + case BASE+14:\ + {\ + /* OP Rd,Rb,Rm ROR # */\ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_ROR_REG\ + } else {\ + LOGICAL_RRX_REG\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+1:\ + {\ + /* OP Rd,Rb,Rm LSL Rs */\ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + if(shift == 32) {\ + value = 0;\ + C_OUT = (reg[opcode & 0x0F].I & 1 ? true : false);\ + } else if(shift < 32) {\ + LOGICAL_LSL_REG\ + } else {\ + value = 0;\ + C_OUT = false;\ + }\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+3:\ + {\ + /* OP Rd,Rb,Rm LSR Rs */ \ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + if(shift == 32) {\ + value = 0;\ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000 ? true : false);\ + } else if(shift < 32) {\ + LOGICAL_LSR_REG\ + } else {\ + value = 0;\ + C_OUT = false;\ + }\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+5:\ + {\ + /* OP Rd,Rb,Rm ASR Rs */ \ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift < 32) {\ + if(shift) {\ + LOGICAL_ASR_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + } else {\ + if(reg[opcode & 0x0F].I & 0x80000000){\ + value = 0xFFFFFFFF;\ + C_OUT = true;\ + } else {\ + value = 0;\ + C_OUT = false;\ + }\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+7:\ + {\ + /* OP Rd,Rb,Rm ROR Rs */\ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + shift &= 0x1f;\ + if(shift) {\ + LOGICAL_ROR_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + C_OUT = (value & 0x80000000 ? true : false);\ + }\ + } else {\ + value = reg[opcode & 0x0F].I;\ + C_OUT = (value & 0x80000000 ? true : false);\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+0x200:\ + case BASE+0x201:\ + case BASE+0x202:\ + case BASE+0x203:\ + case BASE+0x204:\ + case BASE+0x205:\ + case BASE+0x206:\ + case BASE+0x207:\ + case BASE+0x208:\ + case BASE+0x209:\ + case BASE+0x20a:\ + case BASE+0x20b:\ + case BASE+0x20c:\ + case BASE+0x20d:\ + case BASE+0x20e:\ + case BASE+0x20f:\ + {\ + int shift = (opcode & 0xF00) >> 7;\ + int base = (opcode >> 16) & 0x0F;\ + int dest = (opcode >> 12) & 0x0F;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_ROR_IMM\ + } else {\ + value = opcode & 0xff;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break; + +#define LOGICAL_DATA_OPCODE_WITHOUT_base(OPCODE, OPCODE2, BASE) \ + case BASE: \ + case BASE+8:\ + {\ + /* OP Rd,Rb,Rm LSL # */ \ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + \ + if(shift) {\ + LOGICAL_LSL_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+2:\ + case BASE+10:\ + {\ + /* OP Rd,Rb,Rm LSR # */ \ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_LSR_REG\ + } else {\ + value = 0;\ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false;\ + }\ + \ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+4:\ + case BASE+12:\ + {\ + /* OP Rd,Rb,Rm ASR # */\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_ASR_REG\ + } else {\ + if(reg[opcode & 0x0F].I & 0x80000000){\ + value = 0xFFFFFFFF;\ + C_OUT = true;\ + } else {\ + value = 0;\ + C_OUT = false;\ + } \ + }\ + \ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+6:\ + case BASE+14:\ + {\ + /* OP Rd,Rb,Rm ROR # */\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_ROR_REG\ + } else {\ + LOGICAL_RRX_REG\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+1:\ + {\ + /* OP Rd,Rb,Rm LSL Rs */\ + clockTicks++;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + if(shift == 32) {\ + value = 0;\ + C_OUT = (reg[opcode & 0x0F].I & 1 ? true : false);\ + } else if(shift < 32) {\ + LOGICAL_LSL_REG\ + } else {\ + value = 0;\ + C_OUT = false;\ + }\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+3:\ + {\ + /* OP Rd,Rb,Rm LSR Rs */ \ + clockTicks++;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + if(shift == 32) {\ + value = 0;\ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000 ? true : false);\ + } else if(shift < 32) {\ + LOGICAL_LSR_REG\ + } else {\ + value = 0;\ + C_OUT = false;\ + }\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+5:\ + {\ + /* OP Rd,Rb,Rm ASR Rs */ \ + clockTicks++;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift < 32) {\ + if(shift) {\ + LOGICAL_ASR_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + } else {\ + if(reg[opcode & 0x0F].I & 0x80000000){\ + value = 0xFFFFFFFF;\ + C_OUT = true;\ + } else {\ + value = 0;\ + C_OUT = false;\ + }\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+7:\ + {\ + /* OP Rd,Rb,Rm ROR Rs */\ + clockTicks++;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + shift &= 0x1f;\ + if(shift) {\ + LOGICAL_ROR_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + C_OUT = (value & 0x80000000 ? true : false);\ + }\ + } else {\ + value = reg[opcode & 0x0F].I;\ + C_OUT = (value & 0x80000000 ? true : false);\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+0x200:\ + case BASE+0x201:\ + case BASE+0x202:\ + case BASE+0x203:\ + case BASE+0x204:\ + case BASE+0x205:\ + case BASE+0x206:\ + case BASE+0x207:\ + case BASE+0x208:\ + case BASE+0x209:\ + case BASE+0x20a:\ + case BASE+0x20b:\ + case BASE+0x20c:\ + case BASE+0x20d:\ + case BASE+0x20e:\ + case BASE+0x20f:\ + {\ + int shift = (opcode & 0xF00) >> 7;\ + int dest = (opcode >> 12) & 0x0F;\ + bool C_OUT = C_FLAG;\ + u32 value;\ + if(shift) {\ + LOGICAL_ROR_IMM\ + } else {\ + value = opcode & 0xff;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break; + +#define ARITHMETIC_DATA_OPCODE(OPCODE, OPCODE2, BASE) \ + case BASE:\ + case BASE+8:\ + {\ + /* OP Rd,Rb,Rm LSL # */\ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift) {\ + ARITHMETIC_LSL_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+2:\ + case BASE+10:\ + {\ + /* OP Rd,Rb,Rm LSR # */\ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift) {\ + ARITHMETIC_LSR_REG\ + } else {\ + value = 0;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+4:\ + case BASE+12:\ + {\ + /* OP Rd,Rb,Rm ASR # */\ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift) {\ + ARITHMETIC_ASR_REG\ + } else {\ + if(reg[opcode & 0x0F].I & 0x80000000){\ + value = 0xFFFFFFFF;\ + } else value = 0;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+6:\ + case BASE+14:\ + {\ + /* OP Rd,Rb,Rm ROR # */\ + int base = (opcode >> 16) & 0x0F;\ + int shift = (opcode >> 7) & 0x1F;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift) {\ + ARITHMETIC_ROR_REG\ + } else {\ + ARITHMETIC_RRX_REG\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+1:\ + {\ + /* OP Rd,Rb,Rm LSL Rs */\ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift) {\ + if(shift == 32) {\ + value = 0;\ + } else if(shift < 32) {\ + ARITHMETIC_LSL_REG\ + } else value = 0;\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+3:\ + {\ + /* OP Rd,Rb,Rm LSR Rs */\ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift) {\ + if(shift == 32) {\ + value = 0;\ + } else if(shift < 32) {\ + ARITHMETIC_LSR_REG\ + } else value = 0;\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+5:\ + {\ + /* OP Rd,Rb,Rm ASR Rs */\ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift < 32) {\ + if(shift) {\ + ARITHMETIC_ASR_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + } else {\ + if(reg[opcode & 0x0F].I & 0x80000000){\ + value = 0xFFFFFFFF;\ + } else value = 0;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+7:\ + {\ + /* OP Rd,Rb,Rm ROR Rs */\ + clockTicks++;\ + int base = (opcode >> 16) & 0x0F;\ + int shift = reg[(opcode >> 8)&15].B.B0;\ + int dest = (opcode>>12) & 15;\ + u32 value;\ + if(shift) {\ + shift &= 0x1f;\ + if(shift) {\ + ARITHMETIC_ROR_REG\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + } else {\ + value = reg[opcode & 0x0F].I;\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break;\ + case BASE+0x200:\ + case BASE+0x201:\ + case BASE+0x202:\ + case BASE+0x203:\ + case BASE+0x204:\ + case BASE+0x205:\ + case BASE+0x206:\ + case BASE+0x207:\ + case BASE+0x208:\ + case BASE+0x209:\ + case BASE+0x20a:\ + case BASE+0x20b:\ + case BASE+0x20c:\ + case BASE+0x20d:\ + case BASE+0x20e:\ + case BASE+0x20f:\ + {\ + int shift = (opcode & 0xF00) >> 7;\ + int base = (opcode >> 16) & 0x0F;\ + int dest = (opcode >> 12) & 0x0F;\ + u32 value;\ + {\ + ARITHMETIC_ROR_IMM\ + }\ + if(dest == 15) {\ + OPCODE2\ + /* todo */\ + if(opcode & 0x00100000) {\ + clockTicks++;\ + CPUSwitchMode(reg[17].I & 0x1f, false);\ + }\ + if(armState) {\ + reg[15].I &= 0xFFFFFFFC;\ + armNextPC = reg[15].I;\ + reg[15].I += 4;\ + } else {\ + reg[15].I &= 0xFFFFFFFE;\ + armNextPC = reg[15].I;\ + reg[15].I += 2;\ + }\ + } else {\ + OPCODE \ + }\ + }\ + break; + +u32 opcode = CPUReadMemoryQuick(armNextPC); + +clockTicks = memoryWaitFetch32[(armNextPC >> 24) & 15]; + +#ifndef FINAL_VERSION +if(armNextPC == stop) + { + armNextPC++; + } +#endif + +armNextPC = reg[15].I; +reg[15].I += 4; +int cond = opcode >> 28; +// suggested optimization for frequent cases +bool cond_res; +if(cond == 0x0e) + { + cond_res = true; + } +else + { + switch(cond) + { + case 0x00: // EQ + cond_res = Z_FLAG; + break; + case 0x01: // NE + cond_res = !Z_FLAG; + break; + case 0x02: // CS + cond_res = C_FLAG; + break; + case 0x03: // CC + cond_res = !C_FLAG; + break; + case 0x04: // MI + cond_res = N_FLAG; + break; + case 0x05: // PL + cond_res = !N_FLAG; + break; + case 0x06: // VS + cond_res = V_FLAG; + break; + case 0x07: // VC + cond_res = !V_FLAG; + break; + case 0x08: // HI + cond_res = C_FLAG && !Z_FLAG; + break; + case 0x09: // LS + cond_res = !C_FLAG || Z_FLAG; + break; + case 0x0A: // GE + cond_res = N_FLAG == V_FLAG; + break; + case 0x0B: // LT + cond_res = N_FLAG != V_FLAG; + break; + case 0x0C: // GT + cond_res = !Z_FLAG &&(N_FLAG == V_FLAG); + break; + case 0x0D: // LE + cond_res = Z_FLAG || (N_FLAG != V_FLAG); + break; + case 0x0E: + cond_res = true; + break; + case 0x0F: + default: + // ??? + cond_res = false; + break; + } + } + +if(cond_res) + { + switch(((opcode>>16)&0xFF0) | ((opcode>>4)&0x0F)) + { + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_AND, OP_AND, 0x000); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_ANDS, OP_AND, 0x010); + case 0x009: + { + // MUL Rd, Rm, Rs + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs; + if(((s32)rs)<0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x019: + { + // MULS Rd, Rm, Rs + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs; + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; + Z_FLAG = (reg[dest].I) ? false : true; + if(((s32)rs)<0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x00b: + case 0x02b: + { + // STRH Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address -= offset; + reg[base].I = address; + } + break; + case 0x04b: + case 0x06b: + { + // STRH Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address -= offset; + reg[base].I = address; + } + break; + case 0x08b: + case 0x0ab: + { + // STRH Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address += offset; + reg[base].I = address; + } + break; + case 0x0cb: + case 0x0eb: + { + // STRH Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address += offset; + reg[base].I = address; + } + break; + case 0x10b: + { + // STRH Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x12b: + { + // STRH Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x14b: + { + // STRH Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x16b: + { + // STRH Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x18b: + { + // STRH Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x1ab: + { + // STRH Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x1cb: + { + // STRH Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x1eb: + { + // STRH Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x01b: + case 0x03b: + { + // LDRH Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x05b: + case 0x07b: + { + // LDRH Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x09b: + case 0x0bb: + { + // LDRH Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x0db: + case 0x0fb: + { + // LDRH Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x11b: + { + // LDRH Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x13b: + { + // LDRH Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x15b: + { + // LDRH Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x17b: + { + // LDRH Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x19b: + { + // LDRH Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x1bb: + { + // LDRH Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x1db: + { + // LDRH Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x1fb: + { + // LDRH Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x01d: + case 0x03d: + { + // LDRSB Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x05d: + case 0x07d: + { + // LDRSB Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x09d: + case 0x0bd: + { + // LDRSB Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x0dd: + case 0x0fd: + { + // LDRSB Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x11d: + { + // LDRSB Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x13d: + { + // LDRSB Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x15d: + { + // LDRSB Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x17d: + { + // LDRSB Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x19d: + { + // LDRSB Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x1bd: + { + // LDRSB Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x1dd: + { + // LDRSB Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x1fd: + { + // LDRSB Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x01f: + case 0x03f: + { + // LDRSH Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x05f: + case 0x07f: + { + // LDRSH Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x09f: + case 0x0bf: + { + // LDRSH Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x0df: + case 0x0ff: + { + // LDRSH Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x11f: + { + // LDRSH Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x13f: + { + // LDRSH Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x15f: + { + // LDRSH Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x17f: + { + // LDRSH Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x19f: + { + // LDRSH Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x1bf: + { + // LDRSH Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + reg[base].I = address; + } + break; + case 0x1df: + { + // LDRSH Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x1ff: + { + // LDRSH Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if(dest != base) + reg[base].I = address; + } + break; + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_EOR, OP_EOR, 0x020); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_EORS, OP_EOR, 0x030); + case 0x029: + { + // MLA Rd, Rm, Rs, Rn + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs + reg[(opcode>>12)&0x0f].I; + if(((s32)rs)<0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + case 0x039: + { + // MLAS Rd, Rm, Rs, Rn + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs + reg[(opcode>>12)&0x0f].I; + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; + Z_FLAG = (reg[dest].I) ? false : true; + if(((s32)rs)<0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + ARITHMETIC_DATA_OPCODE(OP_SUB, OP_SUB, 0x040); + ARITHMETIC_DATA_OPCODE(OP_SUBS, OP_SUB, 0x050); + ARITHMETIC_DATA_OPCODE(OP_RSB, OP_RSB, 0x060); + ARITHMETIC_DATA_OPCODE(OP_RSBS, OP_RSB, 0x070); + ARITHMETIC_DATA_OPCODE(OP_ADD, OP_ADD, 0x080); + ARITHMETIC_DATA_OPCODE(OP_ADDS, OP_ADD, 0x090); + case 0x089: + { + // UMULL RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = ((u64)umult)*((u64)usource); + reg[destLo].I = (u32)uTemp; + reg[destHi].I = (u32)(uTemp >> 32); + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((usource & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x099: + { + // UMULLS RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = ((u64)umult)*((u64)usource); + reg[destLo].I = (u32)uTemp; + reg[destHi].I = (u32)(uTemp >> 32); + Z_FLAG = (uTemp) ? false : true; + N_FLAG = (reg[destHi].I & 0x80000000) ? true : false; + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((usource & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + ARITHMETIC_DATA_OPCODE(OP_ADC, OP_ADC, 0x0a0); + ARITHMETIC_DATA_OPCODE(OP_ADCS, OP_ADC, 0x0b0); + case 0x0a9: + { + // UMLAL RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = (u64)reg[destHi].I; + uTemp <<= 32; + uTemp |= (u64)reg[destLo].I; + uTemp += ((u64)umult)*((u64)usource); + reg[destLo].I = (u32)uTemp; + reg[destHi].I = (u32)(uTemp >> 32); + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((usource & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + case 0x0b9: + { + // UMLALS RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = (u64)reg[destHi].I; + uTemp <<= 32; + uTemp |= (u64)reg[destLo].I; + uTemp += ((u64)umult)*((u64)usource); + reg[destLo].I = (u32)uTemp; + reg[destHi].I = (u32)(uTemp >> 32); + Z_FLAG = (uTemp) ? false : true; + N_FLAG = (reg[destHi].I & 0x80000000) ? true : false; + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((usource & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + ARITHMETIC_DATA_OPCODE(OP_SBC, OP_SBC, 0x0c0); + ARITHMETIC_DATA_OPCODE(OP_SBCS, OP_SBC, 0x0d0); + case 0x0c9: + { + // SMULL RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = m*s; + reg[destLo].I = (u32)sTemp; + reg[destHi].I = (u32)(sTemp >> 32); + if(((s32)rs) < 0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x0d9: + { + // SMULLS RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = m*s; + reg[destLo].I = (u32)sTemp; + reg[destHi].I = (u32)(sTemp >> 32); + Z_FLAG = (sTemp) ? false : true; + N_FLAG = (sTemp < 0) ? true : false; + if(((s32)rs) < 0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + ARITHMETIC_DATA_OPCODE(OP_RSC, OP_RSC, 0x0e0); + ARITHMETIC_DATA_OPCODE(OP_RSCS, OP_RSC, 0x0f0); + case 0x0e9: + { + // SMLAL RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = (u64)reg[destHi].I; + sTemp <<= 32; + sTemp |= (u64)reg[destLo].I; + sTemp += m*s; + reg[destLo].I = (u32)sTemp; + reg[destHi].I = (u32)(sTemp >> 32); + if(((s32)rs) < 0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + case 0x0f9: + { + // SMLALS RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = (u64)reg[destHi].I; + sTemp <<= 32; + sTemp |= (u64)reg[destLo].I; + sTemp += m*s; + reg[destLo].I = (u32)sTemp; + reg[destHi].I = (u32)(sTemp >> 32); + Z_FLAG = (sTemp) ? false : true; + N_FLAG = (sTemp < 0) ? true : false; + if(((s32)rs) < 0) + rs = ~rs; + if((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + LOGICAL_DATA_OPCODE(OP_TST, OP_TST, 0x110); + case 0x100: + // MRS Rd, CPSR + // TODO: check if right instruction.... + CPUUpdateCPSR(); + reg[(opcode >> 12) & 0x0F].I = reg[16].I; + break; + case 0x109: + { + // SWP Rd, Rm, [Rn] + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadMemory(address); + CPUWriteMemory(address, reg[opcode&15].I); + reg[(opcode >> 12) & 15].I = temp; + } + break; + LOGICAL_DATA_OPCODE(OP_TEQ, OP_TEQ, 0x130); + case 0x120: + { + // MSR CPSR_fields, Rm + CPUUpdateCPSR(); + u32 value = reg[opcode & 15].I; + u32 newValue = reg[16].I; + if(armMode > 0x10) + { + if(opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if(opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if(opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if(opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + newValue |= 0x10; + CPUSwitchMode(newValue & 0x1f, false); + reg[16].I = newValue; + CPUUpdateFlags(); + } + break; + case 0x121: + { + // BX Rm + // TODO: check if right instruction... + clockTicks += 3; + int base = opcode & 0x0F; + armState = reg[base].I & 1 ? false : true; + if(armState) + { + reg[15].I = reg[base].I & 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + else + { + reg[15].I = reg[base].I & 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + } + } + break; + ARITHMETIC_DATA_OPCODE(OP_CMP, OP_CMP, 0x150); + case 0x140: + // MRS Rd, SPSR + // TODO: check if right instruction... + reg[(opcode >> 12) & 0x0F].I = reg[17].I; + break; + case 0x149: + { + // SWPB Rd, Rm, [Rn] + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadByte(address); + CPUWriteByte(address, reg[opcode&15].B.B0); + reg[(opcode>>12)&15].I = temp; + } + break; + ARITHMETIC_DATA_OPCODE(OP_CMN, OP_CMN, 0x170); + case 0x160: + { + // MSR SPSR_fields, Rm + u32 value = reg[opcode & 15].I; + if(armMode > 0x10 && armMode < 0x1f) + { + if(opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if(opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if(opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if(opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + break; + LOGICAL_DATA_OPCODE (OP_ORR, OP_ORR, 0x180); + LOGICAL_DATA_OPCODE (OP_ORRS, OP_ORR, 0x190); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MOV, OP_MOV, 0x1a0); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MOVS, OP_MOV, 0x1b0); + LOGICAL_DATA_OPCODE (OP_BIC, OP_BIC, 0x1c0); + LOGICAL_DATA_OPCODE (OP_BICS, OP_BIC, 0x1d0); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MVN, OP_MVN, 0x1e0); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MVNS, OP_MVN, 0x1f0); +#ifdef BKPT_SUPPORT + case 0x127: + case 0x7ff: // for GDB support + extern void (*dbgSignal)(int,int); + reg[15].I -= 4; + armNextPC -= 4; + dbgSignal(5, (opcode & 0x0f)|((opcode>>4) & 0xfff0)); + return; +#endif + case 0x320: + case 0x321: + case 0x322: + case 0x323: + case 0x324: + case 0x325: + case 0x326: + case 0x327: + case 0x328: + case 0x329: + case 0x32a: + case 0x32b: + case 0x32c: + case 0x32d: + case 0x32e: + case 0x32f: + { + // MSR CPSR_fields, # + CPUUpdateCPSR(); + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if(shift) + { + ROR_IMM_MSR; + } + u32 newValue = reg[16].I; + if(armMode > 0x10) + { + if(opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if(opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if(opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if(opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + + newValue |= 0x10; + + CPUSwitchMode(newValue & 0x1f, false); + reg[16].I = newValue; + CPUUpdateFlags(); + } + break; + case 0x360: + case 0x361: + case 0x362: + case 0x363: + case 0x364: + case 0x365: + case 0x366: + case 0x367: + case 0x368: + case 0x369: + case 0x36a: + case 0x36b: + case 0x36c: + case 0x36d: + case 0x36e: + case 0x36f: + { + // MSR SPSR_fields, # + if(armMode > 0x10 && armMode < 0x1f) + { + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if(shift) + { + ROR_IMM_MSR; + } + if(opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if(opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if(opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if(opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + break; + CASE_16(0x400) + // T versions shouldn't be different on GBA + CASE_16(0x420) + { + // STR Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x480) + // T versions shouldn't be different on GBA + CASE_16(0x4a0) + { + // STR Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x500) + { + // STR Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x520) + { + // STR Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x580) + { + // STR Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x5a0) + { + // STR Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x410) + { + // LDR Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I -= offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x430) + { + // LDRT Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I -= offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x490) + { + // LDR Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I += offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x4b0) + { + // LDRT Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I += offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x510) + { + // LDR Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x530) + { + // LDR Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x590) + { + // LDR Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x5b0) + { + // LDR Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x440) + // T versions shouldn't be different on GBA + CASE_16(0x460) + { + // STRB Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x4c0) + // T versions shouldn't be different on GBA + CASE_16(0x4e0) + // STRB Rd, [Rn], # + { + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x540) + { + // STRB Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x560) + { + // STRB Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5c0) + { + // STRB Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5e0) + { + // STRB Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x450) + // T versions shouldn't be different + CASE_16(0x470) + { + // LDRB Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I -= offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x4d0) + CASE_16(0x4f0) // T versions should not be different + { + // LDRB Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I += offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x550) + { + // LDRB Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x570) + { + // LDRB Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5d0) + { + // LDRB Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5f0) + { + // LDRB Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x600: + case 0x608: + // T versions are the same + case 0x620: + case 0x628: + { + // STR Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x602: + case 0x60a: + // T versions are the same + case 0x622: + case 0x62a: + { + // STR Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x604: + case 0x60c: + // T versions are the same + case 0x624: + case 0x62c: + { + // STR Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x606: + case 0x60e: + // T versions are the same + case 0x626: + case 0x62e: + { + // STR Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - value; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x680: + case 0x688: + // T versions are the same + case 0x6a0: + case 0x6a8: + { + // STR Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x682: + case 0x68a: + // T versions are the same + case 0x6a2: + case 0x6aa: + { + // STR Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x684: + case 0x68c: + // T versions are the same + case 0x6a4: + case 0x6ac: + { + // STR Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x686: + case 0x68e: + // T versions are the same + case 0x6a6: + case 0x6ae: + { + // STR Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + value; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x700: + case 0x708: + { + // STR Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x702: + case 0x70a: + { + // STR Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x704: + case 0x70c: + { + // STR Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x706: + case 0x70e: + { + // STR Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x720: + case 0x728: + { + // STR Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x722: + case 0x72a: + { + // STR Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x724: + case 0x72c: + { + // STR Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x726: + case 0x72e: + { + // STR Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x780: + case 0x788: + { + // STR Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x782: + case 0x78a: + { + // STR Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x784: + case 0x78c: + { + // STR Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x786: + case 0x78e: + { + // STR Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a0: + case 0x7a8: + { + // STR Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a2: + case 0x7aa: + { + // STR Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a4: + case 0x7ac: + { + // STR Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a6: + case 0x7ae: + { + // STR Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x610: + case 0x618: + // T versions are the same + case 0x630: + case 0x638: + { + // LDR Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x612: + case 0x61a: + // T versions are the same + case 0x632: + case 0x63a: + { + // LDR Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x614: + case 0x61c: + // T versions are the same + case 0x634: + case 0x63c: + { + // LDR Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x616: + case 0x61e: + // T versions are the same + case 0x636: + case 0x63e: + { + // LDR Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address - value; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x690: + case 0x698: + // T versions are the same + case 0x6b0: + case 0x6b8: + { + // LDR Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x692: + case 0x69a: + // T versions are the same + case 0x6b2: + case 0x6ba: + { + // LDR Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x694: + case 0x69c: + // T versions are the same + case 0x6b4: + case 0x6bc: + { + // LDR Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x696: + case 0x69e: + // T versions are the same + case 0x6b6: + case 0x6be: + { + // LDR Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address + value; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x710: + case 0x718: + { + // LDR Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x712: + case 0x71a: + { + // LDR Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x714: + case 0x71c: + { + // LDR Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x716: + case 0x71e: + { + // LDR Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x730: + case 0x738: + { + // LDR Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x732: + case 0x73a: + { + // LDR Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x734: + case 0x73c: + { + // LDR Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x736: + case 0x73e: + { + // LDR Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x790: + case 0x798: + { + // LDR Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x792: + case 0x79a: + { + // LDR Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x794: + case 0x79c: + { + // LDR Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x796: + case 0x79e: + { + // LDR Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b0: + case 0x7b8: + { + // LDR Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b2: + case 0x7ba: + { + // LDR Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b4: + case 0x7bc: + { + // LDR Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b6: + case 0x7be: + { + // LDR Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadMemory(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if(dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x640: + case 0x648: + // T versions are the same + case 0x660: + case 0x668: + { + // STRB Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x642: + case 0x64a: + // T versions are the same + case 0x662: + case 0x66a: + { + // STRB Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x644: + case 0x64c: + // T versions are the same + case 0x664: + case 0x66c: + { + // STRB Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x646: + case 0x64e: + // T versions are the same + case 0x666: + case 0x66e: + { + // STRB Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - value; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c0: + case 0x6c8: + // T versions are the same + case 0x6e0: + case 0x6e8: + { + // STRB Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c2: + case 0x6ca: + // T versions are the same + case 0x6e2: + case 0x6ea: + { + // STRB Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c4: + case 0x6cc: + // T versions are the same + case 0x6e4: + case 0x6ec: + { + // STR Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c6: + case 0x6ce: + // T versions are the same + case 0x6e6: + case 0x6ee: + { + // STRB Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + value; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x740: + case 0x748: + { + // STRB Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x742: + case 0x74a: + { + // STRB Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x744: + case 0x74c: + { + // STRB Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x746: + case 0x74e: + { + // STRB Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x760: + case 0x768: + { + // STRB Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x762: + case 0x76a: + { + // STRB Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x764: + case 0x76c: + { + // STRB Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x766: + case 0x76e: + { + // STRB Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c0: + case 0x7c8: + { + // STRB Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c2: + case 0x7ca: + { + // STRB Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c4: + case 0x7cc: + { + // STRB Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c6: + case 0x7ce: + { + // STRB Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e0: + case 0x7e8: + { + // STRB Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e2: + case 0x7ea: + { + // STRB Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e4: + case 0x7ec: + { + // STRB Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e6: + case 0x7ee: + { + // STRB Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x650: + case 0x658: + // T versions are the same + case 0x670: + case 0x678: + { + // LDRB Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x652: + case 0x65a: + // T versions are the same + case 0x672: + case 0x67a: + { + // LDRB Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x654: + case 0x65c: + // T versions are the same + case 0x674: + case 0x67c: + { + // LDRB Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x656: + case 0x65e: + // T versions are the same + case 0x676: + case 0x67e: + { + // LDRB Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address - value; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d0: + case 0x6d8: + // T versions are the same + case 0x6f0: + case 0x6f8: + { + // LDRB Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d2: + case 0x6da: + // T versions are the same + case 0x6f2: + case 0x6fa: + { + // LDRB Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d4: + case 0x6dc: + // T versions are the same + case 0x6f4: + case 0x6fc: + { + // LDRB Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d6: + case 0x6de: + // T versions are the same + case 0x6f6: + case 0x6fe: + { + // LDRB Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address + value; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x750: + case 0x758: + { + // LDRB Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x752: + case 0x75a: + { + // LDRB Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x754: + case 0x75c: + { + // LDRB Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x756: + case 0x75e: + { + // LDRB Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x770: + case 0x778: + { + // LDRB Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x772: + case 0x77a: + { + // LDRB Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x774: + case 0x77c: + { + // LDRB Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x776: + case 0x77e: + { + // LDRB Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d0: + case 0x7d8: + { + // LDRB Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d2: + case 0x7da: + { + // LDRB Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d4: + case 0x7dc: + { + // LDRB Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d6: + case 0x7de: + { + // LDRB Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f0: + case 0x7f8: + { + // LDRB Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode>>7)& 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f2: + case 0x7fa: + { + // LDRB Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f4: + case 0x7fc: + { + // LDRB Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if(shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if(reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f6: + case 0x7fe: + { + // LDRB Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if(shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadByte(address); + if(dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; +#define STMW_REG(val,num) \ +if(opcode & (val)) {\ +CPUWriteMemory(address, reg[(num)].I);\ +if(!offset) {\ +reg[base].I = temp;\ +clockTicks += 1 + CPUUpdateTicksAccess32(address);\ +offset = 1;\ +} else {\ +clockTicks += 1 + CPUUpdateTicksAccessSeq32(address);\ +}\ +address += 4;\ +} +#define STM_REG(val,num) \ +if(opcode & (val)) {\ +CPUWriteMemory(address, reg[(num)].I);\ +if(!offset) {\ +clockTicks += 1 + CPUUpdateTicksAccess32(address);\ +offset = 1;\ +} else {\ +clockTicks += 1 + CPUUpdateTicksAccessSeq32(address);\ +}\ +address += 4;\ +} + + CASE_16(0x800) + // STMDA Rn, {Rlist} + { + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x820) + { + // STMDA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + CASE_16(0x840) + { + // STMDA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + + if(armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x860) + { + // STMDA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + + if(armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + + CASE_16(0x880) + { + // STMIA Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x8a0) + { + // STMIA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x8c0) + { + // STMIA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + if(armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + if(armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x8e0) + { + // STMIA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + if(armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + if(armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + + CASE_16(0x900) + { + // STMDB Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x920) + { + // STMDB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + CASE_16(0x940) + { + // STMDB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + + if(armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x960) + { + // STMDB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + + if(armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + + CASE_16(0x980) + // STMIB Rn, {Rlist} + { + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x9a0) + { + // STMIB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x9c0) + { + // STMIB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + if(armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + if(armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x9e0) + { + // STMIB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + if(armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + if(armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + if(opcode & 32768) + { + CPUWriteMemory(address, reg[15].I+4); + if(!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + +#define LDM_REG(val,num) \ +if(opcode & (val)) {\ +reg[(num)].I = CPUReadMemory(address);\ +if(offset)\ +clockTicks += 1 + CPUUpdateTicksAccessSeq32(address);\ +else {\ +clockTicks += 1 + CPUUpdateTicksAccess32(address);\ +offset = 1;\ +}\ +address += 4;\ +} + + CASE_16(0x810) + { + // LDMDA Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x830) + { + // LDMDA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x850) + { + // LDMDA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x870) + { + // LDMDA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if(!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if(!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + + CASE_16(0x890) + { + // LDMIA Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x8b0) + { + // LDMIA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x8d0) + { + // LDMIA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x8f0) + { + // LDMIA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if(!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if(!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + + CASE_16(0x910) + { + // LDMDB Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x930) + { + // LDMDB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x950) + { + // LDMDB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x970) + { + // LDMDB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if(!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if(!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + + CASE_16(0x990) + { + // LDMIB Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x9b0) + { + // LDMIB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if(opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x9d0) + { + // LDMIB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x9f0) + { + // LDMIB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if(opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if(!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if(!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if(armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if(armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if(armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if(!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + CASE_256(0xa00) + { + // B + clockTicks += 3; + int offset = opcode & 0x00FFFFFF; + if(offset & 0x00800000) + { + offset |= 0xFF000000; + } + offset <<= 2; + reg[15].I += offset; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + CASE_256(0xb00) + { + // BL + clockTicks += 3; + int offset = opcode & 0x00FFFFFF; + if(offset & 0x00800000) + { + offset |= 0xFF000000; + } + offset <<= 2; + reg[14].I = reg[15].I - 4; + reg[15].I += offset; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + CASE_256(0xf00) + // SWI + clockTicks += 3; + CPUSoftwareInterrupt(opcode & 0x00FFFFFF); + break; +#ifdef GP_SUPPORT + case 0xe11: + case 0xe13: + case 0xe15: + case 0xe17: + case 0xe19: + case 0xe1b: + case 0xe1d: + case 0xe1f: + // MRC + break; + case 0xe01: + case 0xe03: + case 0xe05: + case 0xe07: + case 0xe09: + case 0xe0b: + case 0xe0d: + case 0xe0f: + // MRC + break; +#endif + default: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_UNDEFINED) + log("Undefined ARM instruction %08x at %08x\n", opcode, + armNextPC-4); +#endif + CPUUndefinedException(); + break; + // END + } + } diff --git a/src/armdis.cpp b/src/armdis.cpp new file mode 100644 index 0000000..237373f --- /dev/null +++ b/src/armdis.cpp @@ -0,0 +1,809 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/************************************************************************/ +/* Arm/Thumb command set disassembler */ +/************************************************************************/ +#include + +#include "System.h" +#include "Port.h" +#include "GBA.h" +#include "armdis.h" +#include "elf.h" + +struct Opcodes + { + u32 mask; + u32 cval; + char *mnemonic; + }; + +#define debuggerReadMemory(addr) \ + READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadHalfWord(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define debuggerReadByte(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +const char hdig[] = "0123456789abcdef"; + +const char *decVals[16] = + { + "0","1","2","3","4","5","6","7","8", + "9","10","11","12","13","14","15" + }; + +const char *regs[16] = + { + "r0","r1","r2","r3","r4","r5","r6","r7", + "r8","r9","r10","r11","r12","sp","lr","pc" + }; + +const char *conditions[16] = + { + "eq","ne","cs","cc","mi","pl","vs","vc", + "hi","ls","ge","lt","gt","le","","nv" + }; + +const char *shifts[5] = + { + "lsl","lsr","asr","ror","rrx" + }; + +const char *armMultLoadStore[12] = + { + // non-stack + "da","ia","db","ib", + // stack store + "ed","ea","fd","fa", + // stack load + "fa","fd","ea","ed" + }; + +const Opcodes thumbOpcodes[] = + { + // Format 1 + {0xf800, 0x0000, "lsl %r0, %r3, %o"}, + {0xf800, 0x0800, "lsr %r0, %r3, %o"}, + {0xf800, 0x1000, "asr %r0, %r3, %o"}, + // Format 2 + {0xfe00, 0x1800, "add %r0, %r3, %r6"}, + {0xfe00, 0x1a00, "sub %r0, %r3, %r6"}, + {0xfe00, 0x1c00, "add %r0, %r3, %i"}, + {0xfe00, 0x1e00, "sub %r0, %r3, %i"}, + // Format 3 + {0xf800, 0x2000, "mov %r8, %O"}, + {0xf800, 0x2800, "cmp %r8, %O"}, + {0xf800, 0x3000, "add %r8, %O"}, + {0xf800, 0x3800, "sub %r8, %O"}, + // Format 4 + {0xffc0, 0x4000, "and %r0, %r3"}, + {0xffc0, 0x4040, "eor %r0, %r3"}, + {0xffc0, 0x4080, "lsl %r0, %r3"}, + {0xffc0, 0x40c0, "lsr %r0, %r3"}, + {0xffc0, 0x4100, "asr %r0, %r3"}, + {0xffc0, 0x4140, "adc %r0, %r3"}, + {0xffc0, 0x4180, "sbc %r0, %r3"}, + {0xffc0, 0x41c0, "ror %r0, %r3"}, + {0xffc0, 0x4200, "tst %r0, %r3"}, + {0xffc0, 0x4240, "neg %r0, %r3"}, + {0xffc0, 0x4280, "cmp %r0, %r3"}, + {0xffc0, 0x42c0, "cmn %r0, %r3"}, + {0xffc0, 0x4300, "orr %r0, %r3"}, + {0xffc0, 0x4340, "mul %r0, %r3"}, + {0xffc0, 0x4380, "bic %r0, %r3"}, + {0xffc0, 0x43c0, "mvn %r0, %r3"}, + // Format 5 + {0xff80, 0x4700, "bx %h36"}, + {0xfcc0, 0x4400, "[ ??? ]"}, + {0xff00, 0x4400, "add %h07, %h36"}, + {0xff00, 0x4500, "cmp %h07, %h36"}, + {0xff00, 0x4600, "mov %h07, %h36"}, + // Format 6 + {0xf800, 0x4800, "ldr %r8, [%I] (=%J)"}, + // Format 7 + {0xfa00, 0x5000, "str%b %r0, [%r3, %r6]"}, + {0xfa00, 0x5800, "ldr%b %r0, [%r3, %r6]"}, + // Format 8 + {0xfe00, 0x5200, "strh %r0, [%r3, %r6]"}, + {0xfe00, 0x5600, "ldrh %r0, [%r3, %r6]"}, + {0xfe00, 0x5a00, "ldsb %r0, [%r3, %r6]"}, + {0xfe00, 0x5e00, "ldsh %r0, [%r3, %r6]"}, + // Format 9 + {0xe800, 0x6000, "str%B %r0, [%r3, %p]"}, + {0xe800, 0x6800, "ldr%B %r0, [%r3, %p]"}, + // Format 10 + {0xf800, 0x8000, "strh %r0, [%r3, %e]"}, + {0xf800, 0x8800, "ldrh %r0, [%r3, %e]"}, + // Format 11 + {0xf800, 0x9000, "str %r8, [sp, %w]"}, + {0xf800, 0x9800, "ldr %r8, [sp, %w]"}, + // Format 12 + {0xf800, 0xa000, "add %r8, pc, %w (=%K)"}, + {0xf800, 0xa800, "add %r8, sp, %w"}, + // Format 13 + {0xff00, 0xb000, "add sp, %s"}, + // Format 14 + {0xffff, 0xb500, "push {lr}"}, + {0xff00, 0xb400, "push {%l}"}, + {0xff00, 0xb500, "push {%l,lr}"}, + {0xffff, 0xbd00, "pop {pc}"}, + {0xff00, 0xbd00, "pop {%l,pc}"}, + {0xff00, 0xbc00, "pop {%l}"}, + // Format 15 + {0xf800, 0xc000, "stmia %r8!, {%l}"}, + {0xf800, 0xc800, "ldmia %r8!, {%l}"}, + // Format 17 + {0xff00, 0xdf00, "swi %m"}, + // Format 16 + {0xf000, 0xd000, "b%c %W"}, + // Format 18 + {0xf800, 0xe000, "b %a"}, + // Format 19 + {0xf800, 0xf000, "bl %A"}, + {0xf800, 0xf800, "blh %Z"}, + {0xff00, 0xbe00, "bkpt %O"}, + // Unknown + {0x0000, 0x0000, "[ ??? ]"} + }; + +const Opcodes armOpcodes[] = + { + // Undefined + {0x0e000010, 0x06000010, "[ undefined ]"}, + // Branch instructions + {0x0ff000f0, 0x01200010, "bx%c %r0"}, + {0x0f000000, 0x0a000000, "b%c %o"}, + {0x0f000000, 0x0b000000, "bl%c %o"}, + {0x0f000000, 0x0f000000, "swi%c %q"}, + // PSR transfer + {0x0fbf0fff, 0x010f0000, "mrs%c %r3, %p"}, + {0x0db0f000, 0x0120f000, "msr%c %p, %i"}, + // Multiply instructions + {0x0fe000f0, 0x00000090, "mul%c%s %r4, %r0, %r2"}, + {0x0fe000f0, 0x00200090, "mla%c%s %r4, %r0, %r2, %r3"}, + {0x0fa000f0, 0x00800090, "%umull%c%s %r3, %r4, %r0, %r2"}, + {0x0fa000f0, 0x00a00090, "%umlal%c%s %r3, %r4, %r0, %r2"}, + // Load/Store instructions + {0x0fb00ff0, 0x01000090, "swp%c%b %r3, %r0, [%r4]"}, + {0x0fb000f0, 0x01000090, "[ ??? ]"}, + {0x0c100000, 0x04000000, "str%c%b%t %r3, %a"}, + {0x0c100000, 0x04100000, "ldr%c%b%t %r3, %a"}, + {0x0e100090, 0x00000090, "str%c%h %r3, %a"}, + {0x0e100090, 0x00100090, "ldr%c%h %r3, %a"}, + {0x0e100000, 0x08000000, "stm%c%m %r4%l"}, + {0x0e100000, 0x08100000, "ldm%c%m %r4%l"}, + // Data processing + {0x0de00000, 0x00000000, "and%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00200000, "eor%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00400000, "sub%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00600000, "rsb%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00800000, "add%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00a00000, "adc%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00c00000, "sbc%c%s %r3, %r4, %i"}, + {0x0de00000, 0x00e00000, "rsc%c%s %r3, %r4, %i"}, + {0x0de00000, 0x01000000, "tst%c%s %r4, %i"}, + {0x0de00000, 0x01200000, "teq%c%s %r4, %i"}, + {0x0de00000, 0x01400000, "cmp%c%s %r4, %i"}, + {0x0de00000, 0x01600000, "cmn%c%s %r4, %i"}, + {0x0de00000, 0x01800000, "orr%c%s %r3, %r4, %i"}, + {0x0de00000, 0x01a00000, "mov%c%s %r3, %i"}, + {0x0de00000, 0x01c00000, "bic%c%s %r3, %r4, %i"}, + {0x0de00000, 0x01e00000, "mvn%c%s %r3, %i"}, + // Coprocessor operations + {0x0f000010, 0x0e000000, "cdp%c %P, %N, %r3, %R4, %R0%V"}, + {0x0e100000, 0x0c000000, "stc%c%L %P, %r3, %A"}, + {0x0f100010, 0x0e000010, "mcr%c %P, %N, %r3, %R4, %R0%V"}, + {0x0f100010, 0x0e100010, "mrc%c %P, %N, %r3, %R4, %R0%V"}, + // Unknown + {0x00000000, 0x00000000, "[ ??? ]"} + }; + +char* addStr(char *dest, const char *src) +{ + while (*src) + { + *dest++ = *src++; + } + return dest; +} + +char* addHex(char *dest, int siz, u32 val) +{ + if (siz==0) + { + siz = 28; + while ( (((val>>siz)&15)==0) && (siz>=4) ) + siz -= 4; + siz += 4; + } + while (siz>0) + { + siz -= 4; + *dest++ = hdig[(val>>siz)&15]; + } + return dest; +} + +int disArm(u32 offset, char *dest, int flags) +{ + u32 opcode = debuggerReadMemory(offset); + + const Opcodes *sp = armOpcodes; + while( sp->cval != (opcode & sp->mask) ) + sp++; + + if (flags&DIS_VIEW_ADDRESS) + { + dest = addHex(dest, 32, offset); + *dest++ = ' '; + } + if (flags&DIS_VIEW_CODE) + { + dest = addHex(dest, 32, opcode); + *dest++ = ' '; + } + + char *src = sp->mnemonic; + while (*src) + { + if (*src!='%') + *dest++ = *src++; + else + { + src++; + switch (*src) + { + case 'c': + dest = addStr(dest, conditions[opcode>>28]); + break; + case 'r': + dest = addStr(dest, regs[(opcode>>((*(++src)-'0')*4))&15]); + break; + case 'o': + { + *dest++ = '$'; + int off = opcode&0xffffff; + if (off&0x800000) + off |= 0xff000000; + off <<= 2; + dest = addHex(dest, 32, offset+8+off); + } + break; + case 'i': + if (opcode&(1<<25)) + { + dest = addStr(dest, "#0x"); + int imm = opcode&0xff; + int rot = (opcode&0xf00)>>7; + int val = (imm<<(32-rot))|(imm>>rot); + dest = addHex(dest, 0, val); + } + else + { + dest = addStr(dest, regs[opcode&0x0f]); + int shi = (opcode>>5)&3; + int sdw = (opcode>>7)&0x1f; + if ((sdw==0)&&(shi==3)) + shi = 4; + if ( (sdw) || (opcode&0x10) || (shi)) + { + dest = addStr(dest, ", "); + dest = addStr(dest, shifts[shi]); + if (opcode&0x10) + { + *dest++ = ' '; + dest = addStr(dest, regs[(opcode>>8)&15]); + } + else + { + if (sdw==0 && ( (shi==1) || (shi==2) )) + sdw = 32; + if(shi != 4) + { + dest = addStr(dest, " #0x"); + dest = addHex(dest, 8, sdw); + } + } + } + } + break; + case 'p': + if (opcode&(1<<22)) + dest = addStr(dest, "spsr"); + else + dest = addStr(dest, "cpsr"); + if(opcode & 0x00F00000) + { + *dest++ = '_'; + if(opcode & 0x00080000) + *dest++ = 'f'; + if(opcode & 0x00040000) + *dest++ = 's'; + if(opcode & 0x00020000) + *dest++ = 'x'; + if(opcode & 0x00010000) + *dest++ = 'c'; + } + break; + case 's': + if (opcode&(1<<20)) + *dest++ = 's'; + break; + case 'S': + if (opcode&(1<<22)) + *dest++ = 's'; + break; + case 'u': + if (opcode&(1<<22)) + *dest++ = 's'; + else + *dest++ = 'u'; + break; + case 'b': + if (opcode&(1<<22)) + *dest++ = 'b'; + break; + case 'a': + if ((opcode&0x076f0000)==0x004f0000) + { + *dest++ = '['; + *dest++ = '$'; + int adr = offset+8; + int add = (opcode&15)|((opcode>>8)&0xf0); + if (opcode&(1<<23)) + adr += add; + else + adr -= add; + dest = addHex(dest, 32, adr); + *dest++ = ']'; + dest = addStr(dest, " (="); + *dest++ = '$'; + dest = addHex(dest ,32, debuggerReadMemory(adr)); + *dest++=')'; + } + if ((opcode&0x072f0000)==0x050f0000) + { + *dest++ = '['; + *dest++ = '$'; + int adr = offset+8; + if (opcode&(1<<23)) + adr += opcode&0xfff; + else + adr -= opcode&0xfff; + dest = addHex(dest, 32, adr); + *dest++ = ']'; + dest = addStr(dest, " (="); + *dest++ = '$'; + dest = addHex(dest ,32, debuggerReadMemory(adr)); + *dest++=')'; + } + else + { + int reg = (opcode>>16)&15; + *dest++ = '['; + dest = addStr(dest, regs[reg]); + if (!(opcode&(1<<24))) + *dest++ = ']'; + if ( ((opcode&(1<<25))&&(opcode&(1<<26))) || (!(opcode&(1<<22))&&!(opcode&(1<<26))) ) + { + dest = addStr(dest, ", "); + if (!(opcode&(1<<23))) + *dest++ = '-'; + dest = addStr(dest, regs[opcode&0x0f]); + int shi = (opcode>>5)&3; + if (opcode&(1<<26)) + { + if ( ((opcode>>7)&0x1f) || (opcode&0x10) || (shi==1) || (shi==2)) + { + dest = addStr(dest, ", "); + dest = addStr(dest, shifts[shi]); + if (opcode&0x10) + { + *dest++ = ' '; + dest = addStr(dest, regs[(opcode>>8)&15]); + } + else + { + int sdw = (opcode>>7)&0x1f; + if (sdw==0 && ( (shi==1) || (shi==2) )) + sdw = 32; + dest = addStr(dest, " #0x"); + dest = addHex(dest, 8, sdw); + } + } + } + } + else + { + int off; + if (opcode&(1<<26)) + off = opcode&0xfff; + else + off = (opcode&15)|((opcode>>4)&0xf0); + if (off) + { + dest = addStr(dest, ", "); + if (!(opcode&(1<<23))) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, off); + } + } + if (opcode&(1<<24)) + { + *dest++ = ']'; + if (opcode&(1<<21)) + *dest++ = '!'; + } + } + break; + case 't': + if ((opcode&0x01200000)==0x01200000) + *dest++ = 't'; + break; + case 'h': + if (opcode&(1<<6)) + *dest++ = 's'; + if (opcode&(1<<5)) + *dest++ = 'h'; + else + *dest++ = 'b'; + break; + case 'm': + if (((opcode>>16)&15)==13) + { + if(opcode & 0x00100000) + dest = addStr(dest, armMultLoadStore[8+((opcode>>23)&3)]); + else + dest = addStr(dest, armMultLoadStore[4+((opcode>>23)&3)]); + } + else + dest = addStr(dest, armMultLoadStore[(opcode>>23)&3]); + break; + case 'l': + if (opcode&(1<<21)) + *dest++ = '!'; + dest = addStr(dest, ", {"); + { + int rlst = opcode&0xffff; + int msk = 0; + int not_first = 0; + while (msk<16) + { + if (rlst&(1<>8)&15]); + break; + case 'N': + if (opcode&0x10) + dest = addStr(dest, decVals[(opcode>>21)&7]); + else + dest = addStr(dest, decVals[(opcode>>20)&15]); + break; + case 'R': + { + src++; + int reg = 4*(*src-'0'); + *dest++ = 'c'; + dest = addStr(dest, decVals[(opcode>>reg)&15]); + } + break; + case 'V': + { + int val = (opcode>>5)&7; + if (val) + { + dest = addStr(dest, ", "); + dest = addStr(dest, decVals[val]); + } + } + break; + case 'L': + if (opcode&(1<<22)) + *dest++ = 'l'; + break; + case 'A': + if ((opcode&0x012f0000)==0x010f0000) + { + int adr = offset+8; + int add = (opcode&0xff)<<2; + if (opcode&(1<<23)) + adr += add; + else + adr -= add; + *dest++ = '$'; + addHex(dest, 32, adr); + } + else + { + *dest++ = '['; + dest = addStr(dest, regs[(opcode>>16)&15]); + if (!(opcode&(1<<24))) + *dest++ = ']'; + int off = (opcode&0xff)<<2; + if (off) + { + dest = addStr(dest, ", "); + if (!(opcode&(1<<23))) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, off); + } + if (opcode&(1<<24)) + { + *dest++ = ']'; + if (opcode&(1<<21)) + *dest++ = '!'; + } + } + break; + } + src++; + } + } + *dest++ = 0; + + return 4; +} + +int disThumb(u32 offset, char *dest, int flags) +{ + u32 opcode = debuggerReadHalfWord(offset); + + const Opcodes *sp = thumbOpcodes; + int ret = 2; + while( sp->cval != (opcode & sp->mask) ) + sp++; + + if (flags&DIS_VIEW_ADDRESS) + { + dest = addHex(dest, 32, offset); + *dest++ = ' '; + } + if (flags&DIS_VIEW_CODE) + { + dest = addHex(dest, 16, opcode); + *dest++ = ' '; + } + + char *src = sp->mnemonic; + while (*src) + { + if (*src!='%') + *dest++ = *src++; + else + { + src++; + switch (*src) + { + case 'r': + src++; + dest = addStr(dest, regs[(opcode>>(*src-'0'))&7]); + break; + case 'o': + dest = addStr(dest, "#0x"); + { + int val = (opcode>>6)&0x1f; + dest = addHex(dest, 8, val); + } + break; + case 'p': + dest = addStr(dest, "#0x"); + { + int val = (opcode>>6)&0x1f; + if (!(opcode&(1<<12))) + val <<= 2; + dest = addHex(dest, 0, val); + } + break; + case 'e': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, ((opcode>>6)&0x1f)<<1); + break; + case 'i': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode>>6)&7); + break; + case 'h': + { + src++; + int reg = (opcode>>(*src-'0'))&7; + src++; + if (opcode&(1<<(*src-'0'))) + reg += 8; + dest = addStr(dest, regs[reg]); + } + break; + case 'O': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode&0xff)); + break; + case 'I': + *dest++ = '$'; + dest = addHex(dest, 32, (offset&0xfffffffc)+4+((opcode&0xff)<<2)); + break; + case 'J': + { + u32 value = debuggerReadMemory((offset&0xfffffffc)+4+ + ((opcode & 0xff)<<2)); + *dest++ = '$'; + dest = addHex(dest, 32, value); + char *s = elfGetAddressSymbol(value); + if(*s) + { + *dest++ = ' '; + dest = addStr(dest, s); + } + } + break; + case 'K': + { + u32 value = (offset&0xfffffffc)+4+((opcode & 0xff)<<2); + *dest++ = '$'; + dest = addHex(dest, 32, value); + char *s = elfGetAddressSymbol(value); + if(*s) + { + *dest++ = ' '; + dest = addStr(dest, s); + } + } + break; + case 'b': + if (opcode&(1<<10)) + *dest++ = 'b'; + break; + case 'B': + if (opcode&(1<<12)) + *dest++ = 'b'; + break; + case 'w': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode&0xff)<<2); + break; + case 'W': + *dest++ = '$'; + { + int add = opcode&0xff; + if (add&0x80) + add |= 0xffffff00; + dest = addHex(dest, 32, (offset&0xfffffffe)+4+(add<<1)); + } + break; + case 'c': + dest = addStr(dest, conditions[(opcode>>8)&15]); + break; + case 's': + if (opcode&(1<<7)) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode&0x7f)<<2); + break; + case 'l': + { + int rlst = opcode&0xff; + int msk = 0; + int not_first = 0; + while (msk<8) + { + if (rlst&(1<>3) << systemRedShift |\ + ((g) >> 3) << systemGreenShift |\ + ((b) >> 3) << systemBlueShift\ + +static void fill_rgb_row_16(u16 *from, int src_width, u8 *row, int width) +{ + u8 *copy_start = row + src_width*3; + u8 *all_stop = row + width*3; + while (row < copy_start) + { + u16 color = *from++; + *row++ = ((color >> systemRedShift) & 0x1f) << 3; + *row++ = ((color >> systemGreenShift) & 0x1f) << 3; + *row++ = ((color >> systemBlueShift) & 0x1f) << 3; + } + // any remaining elements to be written to 'row' are a replica of the + // preceding pixel + u8 *p = row-3; + while (row < all_stop) + { + // we're guaranteed three elements per pixel; could unroll the loop + // further, especially with a Duff's Device, but the gains would be + // probably limited (judging by profiler output) + *row++ = *p++; + *row++ = *p++; + *row++ = *p++; + } +} + +static void fill_rgb_row_32(u32 *from, int src_width, u8 *row, int width) +{ + u8 *copy_start = row + src_width*3; + u8 *all_stop = row + width*3; + while (row < copy_start) + { + u32 color = *from++; + *row++ = ((color >> systemRedShift) & 0x1f) << 3; + *row++ = ((color >> systemGreenShift) & 0x1f) << 3; + *row++ = ((color >> systemBlueShift) & 0x1f) << 3; + } + // any remaining elements to be written to 'row' are a replica of the + // preceding pixel + u8 *p = row-3; + while (row < all_stop) + { + // we're guaranteed three elements per pixel; could unroll the loop + // further, especially with a Duff's Device, but the gains would be + // probably limited (judging by profiler output) + *row++ = *p++; + *row++ = *p++; + *row++ = *p++; + } +} + +void Bilinear(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u16 *to = (u16 *)dstPtr; + u16 *to_odd = (u16 *)(dstPtr + dstPitch); + + int from_width = width; + u16 *from = (u16 *)srcPtr; + fill_rgb_row_16(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) + { + u16 *from_orig = from; + u16 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_16(from+width+2, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_16(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) + { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + *to++ = RGB(*ar, *ag, *ab); + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u16 *)((u8 *)from_orig + srcPitch); + to = (u16 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u16 *)((u8 *)to + dstPitch); + } +} + +void BilinearPlus(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u16 *to = (u16 *)dstPtr; + u16 *to_odd = (u16 *)(dstPtr + dstPitch); + + int from_width = width; + u16 *from = (u16 *)srcPtr; + fill_rgb_row_16(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) + { + u16 *from_orig = from; + u16 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_16(from+width+2, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_16(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) + { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + //*to++ = manip.rgb(*ar, *ag, *ab); +#ifdef USE_ORIGINAL_BILINEAR_PLUS + *to++ = RGB( + (((*ar)<<2) +((*ar)) + (*cr+*br+*br) )>> 3, + (((*ag)<<2) +((*ag)) + (*cg+*bg+*bg) )>> 3, + (((*ab)<<2) +((*ab)) + (*cb+*bb+*bb) )>> 3); +#else + *to++ = RGB( + (((*ar)<<3) +((*ar)<<1) + (*cr+*br+*br+*cr) )>> 4, + (((*ag)<<3) +((*ag)<<1) + (*cg+*bg+*bg+*cg) )>> 4, + (((*ab)<<3) +((*ab)<<1) + (*cb+*bb+*bb+*cb) )>> 4); +#endif + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u16 *)((u8 *)from_orig + srcPitch); + to = (u16 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u16 *)((u8 *)to + dstPitch); + } +} + +void Bilinear32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *to = (u32 *)dstPtr; + u32 *to_odd = (u32 *)(dstPtr + dstPitch); + + int from_width = width; + if(width+1 < from_width) + from_width = width+1; + u32 *from = (u32 *)srcPtr; + fill_rgb_row_32(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) + { + u32 *from_orig = from; + u32 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_32(from+width+1, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_32(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) + { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + *to++ = RGB(*ar, *ag, *ab); + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u32 *)((u8 *)from_orig + srcPitch); + to = (u32 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u32 *)((u8 *)to + dstPitch); + } +} + +void BilinearPlus32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u32 *to = (u32 *)dstPtr; + u32 *to_odd = (u32 *)(dstPtr + dstPitch); + + int from_width = width; + if(width+1 < from_width) + from_width = width+1; + u32 *from = (u32 *)srcPtr; + fill_rgb_row_32(from, from_width, rgb_row_cur, width+1); + + for(int y = 0; y < height; y++) + { + u32 *from_orig = from; + u32 *to_orig = to; + + if (y+1 < height) + fill_rgb_row_32(from+width+1, from_width, rgb_row_next, + width+1); + else + fill_rgb_row_32(from, from_width, rgb_row_next, width+1); + + // every pixel in the src region, is extended to 4 pixels in the + // destination, arranged in a square 'quad'; if the current src + // pixel is 'a', then in what follows 'b' is the src pixel to the + // right, 'c' is the src pixel below, and 'd' is the src pixel to + // the right and down + u8 *cur_row = rgb_row_cur; + u8 *next_row = rgb_row_next; + u8 *ar = cur_row++; + u8 *ag = cur_row++; + u8 *ab = cur_row++; + u8 *cr = next_row++; + u8 *cg = next_row++; + u8 *cb = next_row++; + for(int x=0; x < width; x++) + { + u8 *br = cur_row++; + u8 *bg = cur_row++; + u8 *bb = cur_row++; + u8 *dr = next_row++; + u8 *dg = next_row++; + u8 *db = next_row++; + + // upper left pixel in quad: just copy it in + //*to++ = manip.rgb(*ar, *ag, *ab); +#ifdef USE_ORIGINAL_BILINEAR_PLUS + *to++ = RGB( + (((*ar)<<2) +((*ar)) + (*cr+*br+*br) )>> 3, + (((*ag)<<2) +((*ag)) + (*cg+*bg+*bg) )>> 3, + (((*ab)<<2) +((*ab)) + (*cb+*bb+*bb) )>> 3); +#else + *to++ = RGB( + (((*ar)<<3) +((*ar)<<1) + (*cr+*br+*br+*cr) )>> 4, + (((*ag)<<3) +((*ag)<<1) + (*cg+*bg+*bg+*cg) )>> 4, + (((*ab)<<3) +((*ab)<<1) + (*cb+*bb+*bb+*cb) )>> 4); +#endif + + // upper right + *to++ = RGB((*ar+*br)>>1, (*ag+*bg)>>1, (*ab+*bb)>>1); + + // lower left + *to_odd++ = RGB((*ar+*cr)>>1, (*ag+*cg)>>1, (*ab+*cb)>>1); + + // lower right + *to_odd++ = RGB((*ar+*br+*cr+*dr)>>2, + (*ag+*bg+*cg+*dg)>>2, + (*ab+*bb+*cb+*db)>>2); + + // 'b' becomes 'a', 'd' becomes 'c' + ar = br; + ag = bg; + ab = bb; + cr = dr; + cg = dg; + cb = db; + } + + // the "next" rgb row becomes the current; the old current rgb row is + // recycled and serves as the new "next" row + u8 *temp; + temp = rgb_row_cur; + rgb_row_cur = rgb_row_next; + rgb_row_next = temp; + + // update the pointers for start of next pair of lines + from = (u32 *)((u8 *)from_orig + srcPitch); + to = (u32 *)((u8 *)to_orig + (dstPitch << 1)); + to_odd = (u32 *)((u8 *)to + dstPitch); + } +} + diff --git a/src/bios.cpp b/src/bios.cpp new file mode 100644 index 0000000..9a3d515 --- /dev/null +++ b/src/bios.cpp @@ -0,0 +1,1293 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include + +#include "GBA.h" +#include "bios.h" +#include "GBAinline.h" +#include "Globals.h" + +s16 sineTable[256] = { + (s16)0x0000, (s16)0x0192, (s16)0x0323, (s16)0x04B5, (s16)0x0645, (s16)0x07D5, (s16)0x0964, (s16)0x0AF1, + (s16)0x0C7C, (s16)0x0E05, (s16)0x0F8C, (s16)0x1111, (s16)0x1294, (s16)0x1413, (s16)0x158F, (s16)0x1708, + (s16)0x187D, (s16)0x19EF, (s16)0x1B5D, (s16)0x1CC6, (s16)0x1E2B, (s16)0x1F8B, (s16)0x20E7, (s16)0x223D, + (s16)0x238E, (s16)0x24DA, (s16)0x261F, (s16)0x275F, (s16)0x2899, (s16)0x29CD, (s16)0x2AFA, (s16)0x2C21, + (s16)0x2D41, (s16)0x2E5A, (s16)0x2F6B, (s16)0x3076, (s16)0x3179, (s16)0x3274, (s16)0x3367, (s16)0x3453, + (s16)0x3536, (s16)0x3612, (s16)0x36E5, (s16)0x37AF, (s16)0x3871, (s16)0x392A, (s16)0x39DA, (s16)0x3A82, + (s16)0x3B20, (s16)0x3BB6, (s16)0x3C42, (s16)0x3CC5, (s16)0x3D3E, (s16)0x3DAE, (s16)0x3E14, (s16)0x3E71, + (s16)0x3EC5, (s16)0x3F0E, (s16)0x3F4E, (s16)0x3F84, (s16)0x3FB1, (s16)0x3FD3, (s16)0x3FEC, (s16)0x3FFB, + (s16)0x4000, (s16)0x3FFB, (s16)0x3FEC, (s16)0x3FD3, (s16)0x3FB1, (s16)0x3F84, (s16)0x3F4E, (s16)0x3F0E, + (s16)0x3EC5, (s16)0x3E71, (s16)0x3E14, (s16)0x3DAE, (s16)0x3D3E, (s16)0x3CC5, (s16)0x3C42, (s16)0x3BB6, + (s16)0x3B20, (s16)0x3A82, (s16)0x39DA, (s16)0x392A, (s16)0x3871, (s16)0x37AF, (s16)0x36E5, (s16)0x3612, + (s16)0x3536, (s16)0x3453, (s16)0x3367, (s16)0x3274, (s16)0x3179, (s16)0x3076, (s16)0x2F6B, (s16)0x2E5A, + (s16)0x2D41, (s16)0x2C21, (s16)0x2AFA, (s16)0x29CD, (s16)0x2899, (s16)0x275F, (s16)0x261F, (s16)0x24DA, + (s16)0x238E, (s16)0x223D, (s16)0x20E7, (s16)0x1F8B, (s16)0x1E2B, (s16)0x1CC6, (s16)0x1B5D, (s16)0x19EF, + (s16)0x187D, (s16)0x1708, (s16)0x158F, (s16)0x1413, (s16)0x1294, (s16)0x1111, (s16)0x0F8C, (s16)0x0E05, + (s16)0x0C7C, (s16)0x0AF1, (s16)0x0964, (s16)0x07D5, (s16)0x0645, (s16)0x04B5, (s16)0x0323, (s16)0x0192, + (s16)0x0000, (s16)0xFE6E, (s16)0xFCDD, (s16)0xFB4B, (s16)0xF9BB, (s16)0xF82B, (s16)0xF69C, (s16)0xF50F, + (s16)0xF384, (s16)0xF1FB, (s16)0xF074, (s16)0xEEEF, (s16)0xED6C, (s16)0xEBED, (s16)0xEA71, (s16)0xE8F8, + (s16)0xE783, (s16)0xE611, (s16)0xE4A3, (s16)0xE33A, (s16)0xE1D5, (s16)0xE075, (s16)0xDF19, (s16)0xDDC3, + (s16)0xDC72, (s16)0xDB26, (s16)0xD9E1, (s16)0xD8A1, (s16)0xD767, (s16)0xD633, (s16)0xD506, (s16)0xD3DF, + (s16)0xD2BF, (s16)0xD1A6, (s16)0xD095, (s16)0xCF8A, (s16)0xCE87, (s16)0xCD8C, (s16)0xCC99, (s16)0xCBAD, + (s16)0xCACA, (s16)0xC9EE, (s16)0xC91B, (s16)0xC851, (s16)0xC78F, (s16)0xC6D6, (s16)0xC626, (s16)0xC57E, + (s16)0xC4E0, (s16)0xC44A, (s16)0xC3BE, (s16)0xC33B, (s16)0xC2C2, (s16)0xC252, (s16)0xC1EC, (s16)0xC18F, + (s16)0xC13B, (s16)0xC0F2, (s16)0xC0B2, (s16)0xC07C, (s16)0xC04F, (s16)0xC02D, (s16)0xC014, (s16)0xC005, + (s16)0xC000, (s16)0xC005, (s16)0xC014, (s16)0xC02D, (s16)0xC04F, (s16)0xC07C, (s16)0xC0B2, (s16)0xC0F2, + (s16)0xC13B, (s16)0xC18F, (s16)0xC1EC, (s16)0xC252, (s16)0xC2C2, (s16)0xC33B, (s16)0xC3BE, (s16)0xC44A, + (s16)0xC4E0, (s16)0xC57E, (s16)0xC626, (s16)0xC6D6, (s16)0xC78F, (s16)0xC851, (s16)0xC91B, (s16)0xC9EE, + (s16)0xCACA, (s16)0xCBAD, (s16)0xCC99, (s16)0xCD8C, (s16)0xCE87, (s16)0xCF8A, (s16)0xD095, (s16)0xD1A6, + (s16)0xD2BF, (s16)0xD3DF, (s16)0xD506, (s16)0xD633, (s16)0xD767, (s16)0xD8A1, (s16)0xD9E1, (s16)0xDB26, + (s16)0xDC72, (s16)0xDDC3, (s16)0xDF19, (s16)0xE075, (s16)0xE1D5, (s16)0xE33A, (s16)0xE4A3, (s16)0xE611, + (s16)0xE783, (s16)0xE8F8, (s16)0xEA71, (s16)0xEBED, (s16)0xED6C, (s16)0xEEEF, (s16)0xF074, (s16)0xF1FB, + (s16)0xF384, (s16)0xF50F, (s16)0xF69C, (s16)0xF82B, (s16)0xF9BB, (s16)0xFB4B, (s16)0xFCDD, (s16)0xFE6E + }; + +void BIOS_ArcTan() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("ArcTan: %08x (VCOUNT=%2d)\n", + reg[0].I, + VCOUNT); + } +#endif + + s32 a = -((s32)(reg[0].I * reg[0].I)) >> 14; + s32 b = ((0xA9 * a) >> 14) + 0x390; + b = ((b * a) >> 14) + 0x91C; + b = ((b * a) >> 14) + 0xFB6; + b = ((b * a) >> 14) + 0x16AA; + b = ((b * a) >> 14) + 0x2081; + b = ((b * a) >> 14) + 0x3651; + b = ((b * a) >> 14) + 0xA2F9; + reg[0].I = (reg[0].I * b) >> 16; + +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("ArcTan: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_ArcTan2() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("ArcTan2: %08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + s16 x = reg[0].I; + s16 y = reg[1].I; + + if (y == 0) + { + reg[0].I = 0x8000 & x; + reg[3].I = 0x170; + } + else + { + if (x == 0) + { + reg[0].I = (0x8000 & y) + 0x4000; + reg[3].I = 0x170; + } + else + { + if (abs(x) > abs(y)) + { + reg[1].I = x; + reg[0].I = y << 14; + BIOS_Div(); + BIOS_ArcTan(); + if (x < 0) + reg[0].I = 0x8000 + reg[0].I; + else + reg[0].I = ((y & 0x8000) << 1 ) + reg[0].I; + reg[3].I = 0x170; + } + else + { + reg[0].I = x << 14; + BIOS_Div(); + BIOS_ArcTan(); + reg[0].I = (0x4000 + (y & 0x8000)) - reg[0].I; + reg[3].I = 0x170; + } + } + } + +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("ArcTan2: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_BitUnPack() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("BitUnPack: %08x,%08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 header = reg[2].I; + + int len = CPUReadHalfWord(header); + // check address + int bits = CPUReadByte(header+2); + int revbits = 8 - bits; + // u32 value = 0; + u32 base = CPUReadMemory(header+4); + bool addBase = (base & 0x80000000) ? true : false; + base &= 0x7fffffff; + int dataSize = CPUReadByte(header+3); + + int data = 0; + int bitwritecount = 0; + while(1) + { + len -= 1; + if(len < 0) + break; + int mask = 0xff >> revbits; + u8 b = CPUReadByte(source); + source++; + int bitcount = 0; + while(1) + { + if(bitcount >= 8) + break; + u32 d = b & mask; + u32 temp = d >> bitcount; + if(!temp && addBase) + { + temp += base; + } + data |= temp << bitwritecount; + bitwritecount += dataSize; + if(bitwritecount >= 32) + { + CPUWriteMemory(dest, data); + dest += 4; + data = 0; + bitwritecount = 0; + } + mask <<= bits; + bitcount += bits; + } + } +} + +void BIOS_BgAffineSet() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("BgAffineSet: %08x,%08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + u32 src = reg[0].I; + u32 dest = reg[1].I; + int num = reg[2].I; + + for(int i = 0; i < num; i++) + { + s32 cx = CPUReadMemory(src); + src+=4; + s32 cy = CPUReadMemory(src); + src+=4; + s16 dispx = CPUReadHalfWord(src); + src+=2; + s16 dispy = CPUReadHalfWord(src); + src+=2; + s16 rx = CPUReadHalfWord(src); + src+=2; + s16 ry = CPUReadHalfWord(src); + src+=2; + u16 theta = CPUReadHalfWord(src)>>8; + src+=4; // keep structure alignment + s32 a = sineTable[(theta+0x40)&255]; + s32 b = sineTable[theta]; + + s16 dx = (rx * a)>>14; + s16 dmx = (rx * b)>>14; + s16 dy = (ry * b)>>14; + s16 dmy = (ry * a)>>14; + + CPUWriteHalfWord(dest, dx); + dest += 2; + CPUWriteHalfWord(dest, -dmx); + dest += 2; + CPUWriteHalfWord(dest, dy); + dest += 2; + CPUWriteHalfWord(dest, dmy); + dest += 2; + + s32 startx = cx - dx * dispx + dmx * dispy; + s32 starty = cy - dy * dispx - dmy * dispy; + + CPUWriteMemory(dest, startx); + dest += 4; + CPUWriteMemory(dest, starty); + dest += 4; + } +} + +void BIOS_CpuSet() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("CpuSet: 0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + reg[2].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 cnt = reg[2].I; + + if(((source & 0xe000000) == 0) || + ((source + (((cnt << 11)>>9) & 0x1fffff)) & 0xe000000) == 0) + return; + + int count = cnt & 0x1FFFFF; + + // 32-bit ? + if((cnt >> 26) & 1) + { + // needed for 32-bit mode! + source &= 0xFFFFFFFC; + dest &= 0xFFFFFFFC; + // fill ? + if((cnt >> 24) & 1) + { + u32 value = CPUReadMemory(source); + while(count) + { + CPUWriteMemory(dest, value); + dest += 4; + count--; + } + } + else + { + // copy + while(count) + { + CPUWriteMemory(dest, CPUReadMemory(source)); + source += 4; + dest += 4; + count--; + } + } + } + else + { + // 16-bit fill? + if((cnt >> 24) & 1) + { + u16 value = CPUReadHalfWord(source); + while(count) + { + CPUWriteHalfWord(dest, value); + dest += 2; + count--; + } + } + else + { + // copy + while(count) + { + CPUWriteHalfWord(dest, CPUReadHalfWord(source)); + source += 2; + dest += 2; + count--; + } + } + } +} + +void BIOS_CpuFastSet() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("CpuFastSet: 0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + reg[2].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 cnt = reg[2].I; + + if(((source & 0xe000000) == 0) || + ((source + (((cnt << 11)>>9) & 0x1fffff)) & 0xe000000) == 0) + return; + + // needed for 32-bit mode! + source &= 0xFFFFFFFC; + dest &= 0xFFFFFFFC; + + int count = cnt & 0x1FFFFF; + + // fill? + if((cnt >> 24) & 1) + { + while(count > 0) + { + // BIOS always transfers 32 bytes at a time + u32 value = CPUReadMemory(source); + for(int i = 0; i < 8; i++) + { + CPUWriteMemory(dest, value); + dest += 4; + } + count -= 8; + } + } + else + { + // copy + while(count > 0) + { + // BIOS always transfers 32 bytes at a time + for(int i = 0; i < 8; i++) + { + CPUWriteMemory(dest, CPUReadMemory(source)); + source += 4; + dest += 4; + } + count -= 8; + } + } +} + +void BIOS_Diff8bitUnFilterWram() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Diff8bitUnFilterWram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff) & 0xe000000) == 0)) + return; + + int len = header >> 8; + + u8 data = CPUReadByte(source++); + CPUWriteByte(dest++, data); + len--; + + while(len > 0) + { + u8 diff = CPUReadByte(source++); + data += diff; + CPUWriteByte(dest++, data); + len--; + } +} + +void BIOS_Diff8bitUnFilterVram() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Diff8bitUnFilterVram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + u8 data = CPUReadByte(source++); + u16 writeData = data; + int shift = 8; + int bytes = 1; + + while(len >= 2) + { + u8 diff = CPUReadByte(source++); + data += diff; + writeData |= (data << shift); + bytes++; + shift += 8; + if(bytes == 2) + { + CPUWriteHalfWord(dest, writeData); + dest += 2; + len -= 2; + bytes = 0; + writeData = 0; + shift = 0; + } + } +} + +void BIOS_Diff16bitUnFilter() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Diff16bitUnFilter: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + u16 data = CPUReadHalfWord(source); + source += 2; + CPUWriteHalfWord(dest, data); + dest += 2; + len -= 2; + + while(len >= 2) + { + u16 diff = CPUReadHalfWord(source); + source += 2; + data += diff; + CPUWriteHalfWord(dest, data); + dest += 2; + len -= 2; + } +} + +void BIOS_Div() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Div: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + int number = reg[0].I; + int denom = reg[1].I; + + if(denom != 0) + { + reg[0].I = number / denom; + reg[1].I = number % denom; + s32 temp = (s32)reg[0].I; + reg[3].I = temp < 0 ? (u32)-temp : (u32)temp; + } +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Div: return=0x%08x,0x%08x,0x%08x\n", + reg[0].I, + reg[1].I, + reg[3].I); + } +#endif +} + +void BIOS_DivARM() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("DivARM: 0x%08x, (VCOUNT=%d)\n", + reg[0].I, + VCOUNT); + } +#endif + + u32 temp = reg[0].I; + reg[0].I = reg[1].I; + reg[1].I = temp; + BIOS_Div(); +} + +void BIOS_HuffUnComp() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("HuffUnComp: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + u8 treeSize = CPUReadByte(source++); + + u32 treeStart = source; + + source += (treeSize<<1) + 1; + + int len = header >> 8; + + u32 mask = 0x80000000; + u32 data = CPUReadMemory(source); + source += 4; + + int pos = 0; + u8 rootNode = CPUReadByte(treeStart); + u8 currentNode = rootNode; + bool writeData = false; + int byteShift = 0; + int byteCount = 0; + u32 writeValue = 0; + + if((header & 0x0F) == 8) + { + while(len > 0) + { + // take left + if(pos == 0) + pos++; + else + pos += (((currentNode & 0x3F)+1)<<1); + + if(data & mask) + { + // right + if(currentNode & 0x40) + writeData = true; + currentNode = CPUReadByte(treeStart+pos+1); + } + else + { + // left + if(currentNode & 0x80) + writeData = true; + currentNode = CPUReadByte(treeStart+pos); + } + + if(writeData) + { + writeValue |= (currentNode << byteShift); + byteCount++; + byteShift += 8; + + pos = 0; + currentNode = rootNode; + writeData = false; + + if(byteCount == 4) + { + byteCount = 0; + byteShift = 0; + CPUWriteMemory(dest, writeValue); + writeValue = 0; + dest += 4; + len -= 4; + } + } + mask >>= 1; + if(mask == 0) + { + mask = 0x80000000; + data = CPUReadMemory(source); + source += 4; + } + } + } + else + { + int halfLen = 0; + int value = 0; + while(len > 0) + { + // take left + if(pos == 0) + pos++; + else + pos += (((currentNode & 0x3F)+1)<<1); + + if((data & mask)) + { + // right + if(currentNode & 0x40) + writeData = true; + currentNode = CPUReadByte(treeStart+pos+1); + } + else + { + // left + if(currentNode & 0x80) + writeData = true; + currentNode = CPUReadByte(treeStart+pos); + } + + if(writeData) + { + if(halfLen == 0) + value |= currentNode; + else + value |= (currentNode<<4); + + halfLen += 4; + if(halfLen == 8) + { + writeValue |= (value << byteShift); + byteCount++; + byteShift += 8; + + halfLen = 0; + value = 0; + + if(byteCount == 4) + { + byteCount = 0; + byteShift = 0; + CPUWriteMemory(dest, writeValue); + dest += 4; + writeValue = 0; + len -= 4; + } + } + pos = 0; + currentNode = rootNode; + writeData = false; + } + mask >>= 1; + if(mask == 0) + { + mask = 0x80000000; + data = CPUReadMemory(source); + source += 4; + } + } + } +} + +void BIOS_LZ77UnCompVram() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("LZ77UnCompVram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int byteCount = 0; + int byteShift = 0; + u32 writeValue = 0; + + int len = header >> 8; + + while(len > 0) + { + u8 d = CPUReadByte(source++); + + if(d) + { + for(int i = 0; i < 8; i++) + { + if(d & 0x80) + { + u16 data = CPUReadByte(source++) << 8; + data |= CPUReadByte(source++); + int length = (data >> 12) + 3; + int offset = (data & 0x0FFF); + u32 windowOffset = dest + byteCount - offset - 1; + for(int i = 0; i < length; i++) + { + writeValue |= (CPUReadByte(windowOffset++) << byteShift); + byteShift += 8; + byteCount++; + + if(byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } + else + { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if(byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + d <<= 1; + } + } + else + { + for(int i = 0; i < 8; i++) + { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if(byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteShift = 0; + byteCount = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_LZ77UnCompWram() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("LZ77UnCompWram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + while(len > 0) + { + u8 d = CPUReadByte(source++); + + if(d) + { + for(int i = 0; i < 8; i++) + { + if(d & 0x80) + { + u16 data = CPUReadByte(source++) << 8; + data |= CPUReadByte(source++); + int length = (data >> 12) + 3; + int offset = (data & 0x0FFF); + u32 windowOffset = dest - offset - 1; + for(int i = 0; i < length; i++) + { + CPUWriteByte(dest++, CPUReadByte(windowOffset++)); + len--; + if(len == 0) + return; + } + } + else + { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if(len == 0) + return; + } + d <<= 1; + } + } + else + { + for(int i = 0; i < 8; i++) + { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_ObjAffineSet() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("ObjAffineSet: 0x%08x,0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + reg[3].I, + VCOUNT); + } +#endif + + u32 src = reg[0].I; + u32 dest = reg[1].I; + int num = reg[2].I; + int offset = reg[3].I; + + for(int i = 0; i < num; i++) + { + s16 rx = CPUReadHalfWord(src); + src+=2; + s16 ry = CPUReadHalfWord(src); + src+=2; + u16 theta = CPUReadHalfWord(src)>>8; + src+=4; // keep structure alignment + + s32 a = (s32)sineTable[(theta+0x40)&255]; + s32 b = (s32)sineTable[theta]; + + s16 dx = ((s32)rx * a)>>14; + s16 dmx = ((s32)rx * b)>>14; + s16 dy = ((s32)ry * b)>>14; + s16 dmy = ((s32)ry * a)>>14; + + CPUWriteHalfWord(dest, dx); + dest += offset; + CPUWriteHalfWord(dest, -dmx); + dest += offset; + CPUWriteHalfWord(dest, dy); + dest += offset; + CPUWriteHalfWord(dest, dmy); + dest += offset; + } +} + +void BIOS_RegisterRamReset(u32 flags) +{ + // no need to trace here. this is only called directly from GBA.cpp + // to emulate bios initialization + + if(flags) + { + if(flags & 0x01) + { + // clear work RAM + memset(workRAM, 0, 0x40000); + } + if(flags & 0x02) + { + // clear internal RAM + memset(internalRAM, 0, 0x7e00); // don't clear 0x7e00-0x7fff + } + if(flags & 0x04) + { + // clear palette RAM + memset(paletteRAM, 0, 0x400); + } + if(flags & 0x08) + { + // clear VRAM + memset(vram, 0, 0x18000); + } + if(flags & 0x10) + { + // clean OAM + memset(oam, 0, 0x400); + } + + if(flags & 0x80) + { + int i; + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x200+i*2, 0); + + CPUUpdateRegister(0x202, 0xFFFF); + + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x4+i*2, 0); + + for(i = 0; i < 16; i++) + CPUUpdateRegister(0x20+i*2, 0); + + for(i = 0; i < 24; i++) + CPUUpdateRegister(0xb0+i*2, 0); + + CPUUpdateRegister(0x130, 0); + CPUUpdateRegister(0x20, 0x100); + CPUUpdateRegister(0x30, 0x100); + CPUUpdateRegister(0x26, 0x100); + CPUUpdateRegister(0x36, 0x100); + } + + if(flags & 0x20) + { + int i; + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x110+i*2, 0); + CPUUpdateRegister(0x134, 0x8000); + for(i = 0; i < 7; i++) + CPUUpdateRegister(0x140+i*2, 0); + } + + if(flags & 0x40) + { + int i; + CPUWriteByte(0x4000084, 0); + CPUWriteByte(0x4000084, 0x80); + CPUWriteMemory(0x4000080, 0x880e0000); + CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); + CPUWriteByte(0x4000070, 0x70); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000070, 0); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000084, 0); + } + } +} + +void BIOS_RegisterRamReset() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("RegisterRamReset: 0x%08x (VCOUNT=%d)\n", + reg[0].I, + VCOUNT); + } +#endif + + BIOS_RegisterRamReset(reg[0].I); +} + +void BIOS_RLUnCompVram() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("RLUnCompVram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + int byteCount = 0; + int byteShift = 0; + u32 writeValue = 0; + + while(len > 0) + { + u8 d = CPUReadByte(source++); + int l = d & 0x7F; + if(d & 0x80) + { + u8 data = CPUReadByte(source++); + l += 3; + for(int i = 0;i < l; i++) + { + writeValue |= (data << byteShift); + byteShift += 8; + byteCount++; + + if(byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } + else + { + l++; + for(int i = 0; i < l; i++) + { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if(byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_RLUnCompWram() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("RLUnCompWram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if(((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + while(len > 0) + { + u8 d = CPUReadByte(source++); + int l = d & 0x7F; + if(d & 0x80) + { + u8 data = CPUReadByte(source++); + l += 3; + for(int i = 0;i < l; i++) + { + CPUWriteByte(dest++, data); + len--; + if(len == 0) + return; + } + } + else + { + l++; + for(int i = 0; i < l; i++) + { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if(len == 0) + return; + } + } + } +} + +void BIOS_SoftReset() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("SoftReset: (VCOUNT=%d)\n", VCOUNT); + } +#endif + + armState = true; + armMode = 0x1F; + armIrqEnable = false; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + reg[13].I = 0x03007F00; + reg[14].I = 0x00000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R14_IRQ].I = 0x00000000; + reg[SPSR_IRQ].I = 0x00000000; + reg[R13_SVC].I = 0x03007FE0; + reg[R14_SVC].I = 0x00000000; + reg[SPSR_SVC].I = 0x00000000; + u8 b = internalRAM[0x7ffa]; + + memset(&internalRAM[0x7e00], 0, 0x200); + + if(b) + { + armNextPC = 0x02000000; + reg[15].I = 0x02000004; + } + else + { + armNextPC = 0x08000000; + reg[15].I = 0x08000004; + } +} + +void BIOS_Sqrt() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Sqrt: %08x (VCOUNT=%2d)\n", + reg[0].I, + VCOUNT); + } +#endif + reg[0].I = (u32)sqrt((double)reg[0].I); +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("Sqrt: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_MidiKey2Freq() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("MidiKey2Freq: WaveData=%08x mk=%08x fp=%08x\n", + reg[0].I, + reg[1].I, + reg[2].I); + } +#endif + int freq = CPUReadMemory(reg[0].I+4); + double tmp; + tmp = ((double)(180 - reg[1].I)) - ((double)reg[2].I / 256.f); + tmp = pow((double)2.f, tmp / 12.f); + reg[0].I = (int)((double)freq / tmp); + +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("MidiKey2Freq: return %08x\n", + reg[0].I); + } +#endif +} + +void BIOS_SndDriverJmpTableCopy() +{ +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_SWI) + { + log("SndDriverJmpTableCopy: dest=%08x\n", + reg[0].I); + } +#endif + for(int i = 0; i < 0x24; i++) + { + CPUWriteMemory(reg[0].I, 0x9c); + reg[0].I += 4; + } +} diff --git a/src/bios.h b/src/bios.h new file mode 100644 index 0000000..58b3417 --- /dev/null +++ b/src/bios.h @@ -0,0 +1,46 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_BIOS_H +#define VBA_BIOS_H + +extern void BIOS_ArcTan(); +extern void BIOS_ArcTan2(); +extern void BIOS_BitUnPack(); +extern void BIOS_BgAffineSet(); +extern void BIOS_CpuSet(); +extern void BIOS_CpuFastSet(); +extern void BIOS_Diff8bitUnFilterWram(); +extern void BIOS_Diff8bitUnFilterVram(); +extern void BIOS_Diff16bitUnFilter(); +extern void BIOS_Div(); +extern void BIOS_DivARM(); +extern void BIOS_HuffUnComp(); +extern void BIOS_LZ77UnCompVram(); +extern void BIOS_LZ77UnCompWram(); +extern void BIOS_ObjAffineSet(); +extern void BIOS_RegisterRamReset(); +extern void BIOS_RegisterRamReset(u32); +extern void BIOS_RLUnCompVram(); +extern void BIOS_RLUnCompWram(); +extern void BIOS_SoftReset(); +extern void BIOS_Sqrt(); +extern void BIOS_MidiKey2Freq(); +extern void BIOS_SndDriverJmpTableCopy(); +#endif // VBA_BIOS_H diff --git a/src/elf.cpp b/src/elf.cpp new file mode 100644 index 0000000..25cce47 --- /dev/null +++ b/src/elf.cpp @@ -0,0 +1,3248 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "sdfileio.h" +#include +#include +#include + +#include "GBA.h" +#include "Port.h" +#include "elf.h" +#include "NLS.h" + +#define elfReadMemory(addr) \ + READ32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define DW_TAG_array_type 0x01 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_label 0x0a +#define DW_TAG_lexical_block 0x0b +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_inheritance 0x1c +#define DW_TAG_inlined_subroutine 0x1d +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_base_type 0x24 +#define DW_TAG_const_type 0x26 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_subprogram 0x2e +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 + +#define DW_AT_sibling 0x01 +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_byte_size 0x0b +#define DW_AT_bit_offset 0x0c +#define DW_AT_bit_size 0x0d +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_compdir 0x1b +#define DW_AT_const_value 0x1c +#define DW_AT_containing_type 0x1d +#define DW_AT_inline 0x20 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_upper_bound 0x2f +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_artificial 0x34 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_file 0x3a +#define DW_AT_decl_line 0x3b +#define DW_AT_declaration 0x3c +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_frame_base 0x40 +#define DW_AT_macro_info 0x43 +#define DW_AT_specification 0x47 +#define DW_AT_type 0x49 +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d +// DWARF 2.1/3.0 extensions +#define DW_AT_entry_pc 0x52 +#define DW_AT_ranges 0x55 +// ARM Compiler extensions +#define DW_AT_proc_body 0x2000 +#define DW_AT_save_offset 0x2001 +#define DW_AT_user_2002 0x2002 +// MIPS extensions +#define DW_AT_MIPS_linkage_name 0x2007 + +#define DW_FORM_addr 0x01 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_string 0x08 +#define DW_FORM_block 0x09 +#define DW_FORM_block1 0x0a +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_strp 0x0e +#define DW_FORM_udata 0x0f +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 + +#define DW_OP_addr 0x03 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_reg0 0x50 +#define DW_OP_reg1 0x51 +#define DW_OP_reg2 0x52 +#define DW_OP_reg3 0x53 +#define DW_OP_reg4 0x54 +#define DW_OP_reg5 0x55 +#define DW_OP_reg6 0x56 +#define DW_OP_reg7 0x57 +#define DW_OP_reg8 0x58 +#define DW_OP_reg9 0x59 +#define DW_OP_reg10 0x5a +#define DW_OP_reg11 0x5b +#define DW_OP_reg12 0x5c +#define DW_OP_reg13 0x5d +#define DW_OP_reg14 0x5e +#define DW_OP_reg15 0x5f +#define DW_OP_fbreg 0x91 + +#define DW_LNS_extended_op 0x00 +#define DW_LNS_copy 0x01 +#define DW_LNS_advance_pc 0x02 +#define DW_LNS_advance_line 0x03 +#define DW_LNS_set_file 0x04 +#define DW_LNS_set_column 0x05 +#define DW_LNS_negate_stmt 0x06 +#define DW_LNS_set_basic_block 0x07 +#define DW_LNS_const_add_pc 0x08 +#define DW_LNS_fixed_advance_pc 0x09 + +#define DW_LNE_end_sequence 0x01 +#define DW_LNE_set_address 0x02 +#define DW_LNE_define_file 0x03 + +#define DW_CFA_advance_loc 0x01 +#define DW_CFA_offset 0x02 +#define DW_CFA_restore 0x03 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_nop 0x00 + +#define CASE_TYPE_TAG \ + case DW_TAG_const_type:\ + case DW_TAG_volatile_type:\ + case DW_TAG_pointer_type:\ + case DW_TAG_base_type:\ + case DW_TAG_array_type:\ + case DW_TAG_structure_type:\ + case DW_TAG_union_type:\ + case DW_TAG_typedef:\ + case DW_TAG_subroutine_type:\ + case DW_TAG_enumeration_type:\ + case DW_TAG_enumerator:\ + case DW_TAG_reference_type + +struct ELFcie + { + ELFcie *next; + u32 offset; + u8 *augmentation; + u32 codeAlign; + s32 dataAlign; + int returnAddress; + u8 *data; + u32 dataLen; + }; + +struct ELFfde + { + ELFcie *cie; + u32 address; + u32 end; + u8 *data; + u32 dataLen; + }; + +enum ELFRegMode { + REG_NOT_SET, + REG_OFFSET, + REG_REGISTER +}; + + +struct ELFFrameStateRegister + { + ELFRegMode mode; + int reg; + s32 offset; + }; + +struct ELFFrameStateRegisters + { + ELFFrameStateRegister regs[16]; + ELFFrameStateRegisters *previous; + }; + +enum ELFCfaMode { + CFA_NOT_SET, + CFA_REG_OFFSET +}; + +struct ELFFrameState + { + ELFFrameStateRegisters registers; + + ELFCfaMode cfaMode; + int cfaRegister; + s32 cfaOffset; + + u32 pc; + + int dataAlign; + int codeAlign; + int returnAddress; + }; + +extern bool cpuIsMultiBoot; + +Symbol *elfSymbols = NULL; +char *elfSymbolsStrTab = NULL; +int elfSymbolsCount = 0; + +ELFSectionHeader **elfSectionHeaders = NULL; +char *elfSectionHeadersStringTable = NULL; +int elfSectionHeadersCount = 0; +u8 *elfFileData = NULL; + +CompileUnit *elfCompileUnits = NULL; +DebugInfo *elfDebugInfo = NULL; +char *elfDebugStrings = NULL; + +ELFcie *elfCies = NULL; +ELFfde **elfFdes = NULL; +int elfFdeCount = 0; + +CompileUnit *elfCurrentUnit = NULL; + +u32 elfRead4Bytes(u8 *); +u16 elfRead2Bytes(u8 *); + +CompileUnit *elfGetCompileUnit(u32 addr) +{ + if(elfCompileUnits) + { + CompileUnit *unit = elfCompileUnits; + while(unit) + { + if(unit->lowPC) + { + if(addr >= unit->lowPC && addr < unit->highPC) + return unit; + } + else + { + ARanges *r = unit->ranges; + if(r) + { + int count = r->count; + for(int j = 0; j < count; j++) + { + if(addr >= r->ranges[j].lowPC && addr < r->ranges[j].highPC) + return unit; + } + } + } + unit = unit->next; + } + } + return NULL; +} + +char *elfGetAddressSymbol(u32 addr) +{ + static char buffer[256]; + + CompileUnit *unit = elfGetCompileUnit(addr); + // found unit, need to find function + if(unit) + { + Function *func = unit->functions; + while(func) + { + if(addr >= func->lowPC && addr < func->highPC) + { + int offset = addr - func->lowPC; + char *name = func->name; + if(!name) + name = ""; + if(offset) + sprintf(buffer, "%s+%d", name, offset); + else + strcpy(buffer, name); + return buffer; + } + func = func->next; + } + } + + if(elfSymbolsCount) + { + for(int i = 0; i < elfSymbolsCount; i++) + { + Symbol *s = &elfSymbols[i]; + if((addr >= s->value) && addr < (s->value+s->size)) + { + int offset = addr-s->value; + char *name = s->name; + if(name == NULL) + name = ""; + if(offset) + sprintf(buffer, "%s+%d", name, addr-s->value); + else + strcpy(buffer, name); + return buffer; + } + else if(addr == s->value) + { + if(s->name) + strcpy(buffer, s->name); + else + strcpy(buffer, ""); + return buffer; + } + } + } + + return ""; +} + +bool elfFindLineInModule(u32 *addr, char *name, int line) +{ + CompileUnit *unit = elfCompileUnits; + + while(unit) + { + if(unit->lineInfoTable) + { + int i; + int count = unit->lineInfoTable->fileCount; + char *found = NULL; + for(i = 0; i < count; i++) + { + if(strcmp(name, unit->lineInfoTable->files[i]) == 0) + { + found = unit->lineInfoTable->files[i]; + break; + } + } + // found a matching filename... try to find line now + if(found) + { + LineInfoItem *table = unit->lineInfoTable->lines; + count = unit->lineInfoTable->number; + for(i = 0; i < count; i++) + { + if(table[i].file == found && table[i].line == line) + { + *addr = table[i].address; + return true; + } + } + // we can only find a single match + return false; + } + } + unit = unit->next; + } + return false; +} + +int elfFindLine(CompileUnit *unit, Function * /* func */, u32 addr, char **f) +{ + int currentLine = -1; + if(unit->hasLineInfo) + { + int count = unit->lineInfoTable->number; + LineInfoItem *table = unit->lineInfoTable->lines; + int i; + for(i = 0; i < count; i++) + { + if(addr <= table[i].address) + break; + } + if(i == count) + i--; + *f = table[i].file; + currentLine = table[i].line; + } + return currentLine; +} + +bool elfFindLineInUnit(u32 *addr, CompileUnit *unit, int line) +{ + if(unit->hasLineInfo) + { + int count = unit->lineInfoTable->number; + LineInfoItem *table = unit->lineInfoTable->lines; + int i; + for(i = 0; i < count; i++) + { + if(line == table[i].line) + { + *addr = table[i].address; + return true; + } + } + } + return false; +} + +bool elfGetCurrentFunction(u32 addr, Function **f, CompileUnit **u) +{ + CompileUnit *unit = elfGetCompileUnit(addr); + // found unit, need to find function + if(unit) + { + Function *func = unit->functions; + while(func) + { + if(addr >= func->lowPC && addr < func->highPC) + { + *f = func; + *u = unit; + return true; + } + func = func->next; + } + } + return false; +} + +bool elfGetObject(char *name, Function *f, CompileUnit *u, Object **o) +{ + if(f && u) + { + Object *v = f->variables; + + while(v) + { + if(strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + v = f->parameters; + while(v) + { + if(strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + v = u->variables; + while(v) + { + if(strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + } + + CompileUnit *c = elfCompileUnits; + + while(c) + { + if(c != u) + { + Object *v = c->variables; + while(v) + { + if(strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + } + c = c->next; + } + + return false; +} + +char *elfGetSymbol(int i, u32 *value, u32 *size, int *type) +{ + if(i < elfSymbolsCount) + { + Symbol *s = &elfSymbols[i]; + *value = s->value; + *size = s->size; + *type = s->type; + return s->name; + } + return NULL; +} + +bool elfGetSymbolAddress(char *sym, u32 *addr, u32 *size, int *type) +{ + if(elfSymbolsCount) + { + for(int i = 0; i < elfSymbolsCount; i++) + { + Symbol *s = &elfSymbols[i]; + if(strcmp(sym, s->name) == 0) + { + *addr = s->value; + *size = s->size; + *type = s->type; + return true; + } + } + } + return false; +} + +ELFfde *elfGetFde(u32 address) +{ + if(elfFdes) + { + int i; + for(i = 0; i < elfFdeCount; i++) + { + if(address >= elfFdes[i]->address && + address < elfFdes[i]->end) + { + return elfFdes[i]; + } + } + } + + return NULL; +} + +void elfExecuteCFAInstructions(ELFFrameState *state, u8 *data, u32 len, + u32 pc) +{ + u8 *end = data + len; + int bytes; + int reg; + ELFFrameStateRegisters *fs; + + while(data < end && state->pc < pc) + { + u8 op = *data++; + + switch(op >> 6) + { + case DW_CFA_advance_loc: + state->pc += (op & 0x3f) * state->codeAlign; + break; + case DW_CFA_offset: + reg = op & 0x3f; + state->registers.regs[reg].mode = REG_OFFSET; + state->registers.regs[reg].offset = state->dataAlign * + (s32)elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_restore: + // we don't care much about the other possible settings, + // so just setting to unset is enough for now + state->registers.regs[op & 0x3f].mode = REG_NOT_SET; + break; + case 0: + switch(op & 0x3f) + { + case DW_CFA_nop: + break; + case DW_CFA_advance_loc1: + state->pc += state->codeAlign * (*data++); + break; + case DW_CFA_advance_loc2: + state->pc += state->codeAlign * elfRead2Bytes(data); + data += 2; + break; + case DW_CFA_advance_loc4: + state->pc += state->codeAlign * elfRead4Bytes(data); + data += 4; + break; + case DW_CFA_offset_extended: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_OFFSET; + state->registers.regs[reg].offset = state->dataAlign * + (s32)elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_NOT_SET; + break; + case DW_CFA_register: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_REGISTER; + state->registers.regs[reg].reg = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_remember_state: + fs = (ELFFrameStateRegisters *)calloc(1, + sizeof(ELFFrameStateRegisters)); + memcpy(fs, &state->registers, sizeof(ELFFrameStateRegisters)); + state->registers.previous = fs; + break; + case DW_CFA_restore_state: + if(state->registers.previous == NULL) + { + printf("Error: previous frame state is NULL.\n"); + return; + } + fs = state->registers.previous; + memcpy(&state->registers, fs, sizeof(ELFFrameStateRegisters)); + free(fs); + break; + case DW_CFA_def_cfa: + state->cfaRegister = elfReadLEB128(data, &bytes); + data += bytes; + state->cfaOffset = (s32)elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + case DW_CFA_def_cfa_register: + state->cfaRegister = elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + case DW_CFA_def_cfa_offset: + state->cfaOffset = (s32)elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + default: + printf("Unknown CFA opcode %08x\n", op); + return; + } + break; + default: + printf("Unknown CFA opcode %08x\n", op); + return; + } + } +} + +ELFFrameState *elfGetFrameState(ELFfde *fde, u32 address) +{ + ELFFrameState *state = (ELFFrameState *)calloc(1, sizeof(ELFFrameState)); + state->pc = fde->address; + state->dataAlign = fde->cie->dataAlign; + state->codeAlign = fde->cie->codeAlign; + state->returnAddress = fde->cie->returnAddress; + + elfExecuteCFAInstructions(state, + fde->cie->data, + fde->cie->dataLen, + 0xffffffff); + elfExecuteCFAInstructions(state, + fde->data, + fde->dataLen, + address); + + return state; +} + +void elfPrintCallChain(u32 address) +{ + int count = 1; + + reg_pair regs[15]; + reg_pair newRegs[15]; + + memcpy(®s[0], ®[0], sizeof(reg_pair) * 15); + + while(count < 20) + { + char *addr = elfGetAddressSymbol(address); + if(*addr == 0) + addr = "???"; + + printf("%08x %s\n", address, addr); + + ELFfde *fde = elfGetFde(address); + + if(fde == NULL) + { + break; + } + + ELFFrameState *state = elfGetFrameState(fde, address); + + if(!state) + { + break; + } + + if(state->cfaMode == CFA_REG_OFFSET) + { + memcpy(&newRegs[0], ®s[0], sizeof(reg_pair) * 15); + u32 addr = 0; + for(int i = 0; i < 15; i++) + { + ELFFrameStateRegister *r = &state->registers. + regs[i]; + + switch(r->mode) + { + case REG_NOT_SET: + newRegs[i].I = regs[i].I; + break; + case REG_OFFSET: + newRegs[i].I = elfReadMemory(regs[state->cfaRegister].I + + state->cfaOffset + + r->offset); + break; + case REG_REGISTER: + newRegs[i].I = regs[r->reg].I; + break; + default: + printf("Unknown register mode: %d\n", r->mode); + break; + } + } + memcpy(regs, newRegs, sizeof(reg_pair)*15); + addr = newRegs[14].I; + addr &= 0xfffffffe; + address = addr; + count++; + } + else + { + printf("CFA not set\n"); + break; + } + if(state->registers.previous) + { + ELFFrameStateRegisters *prev = state->registers.previous; + + while(prev) + { + ELFFrameStateRegisters *p = prev->previous; + free(prev); + prev = p; + } + } + free(state); + } +} + +u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type, u32 base) +{ + u32 framebase = 0; + if(f && f->frameBase) + { + ELFBlock *b = f->frameBase; + switch(*b->data) + { + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + framebase = reg[*b->data-0x50].I; + break; + default: + fprintf(stderr, "Unknown frameBase %02x\n", *b->data); + break; + } + } + + ELFBlock *loc = o; + u32 location = 0; + int bytes = 0; + if(loc) + { + switch(*loc->data) + { + case DW_OP_addr: + location = elfRead4Bytes(loc->data+1); + *type = LOCATION_memory; + break; + case DW_OP_plus_uconst: + location = base + elfReadLEB128(loc->data+1, &bytes); + *type = LOCATION_memory; + break; + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + location = *loc->data - 0x50; + *type = LOCATION_register; + break; + case DW_OP_fbreg: + { + int bytes; + s32 off = elfReadSignedLEB128(loc->data+1, &bytes); + location = framebase + off; + *type = LOCATION_memory; + } + break; + default: + fprintf(stderr, "Unknown location %02x\n", *loc->data); + break; + } + } + return location; +} + +u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type) +{ + return elfDecodeLocation(f, o, type, 0); +} + +// reading function + +u32 elfRead4Bytes(u8 *data) +{ + u32 value = *data++; + value |= (*data++ << 8); + value |= (*data++ << 16); + value |= (*data << 24); + return value; +} + +u16 elfRead2Bytes(u8 *data) +{ + u16 value = *data++; + value |= (*data << 8); + return value; +} + +char *elfReadString(u8 *data, int *bytesRead) +{ + if(*data == 0) + { + *bytesRead = 1; + return NULL; + } + *bytesRead = strlen((char *)data) + 1; + return (char *)data; +} + +s32 elfReadSignedLEB128(u8 *data, int *bytesRead) +{ + s32 result = 0; + int shift = 0; + int count = 0; + + u8 byte; + do + { + byte = *data++; + count++; + result |= (byte & 0x7f) << shift; + shift += 7; + } + while(byte & 0x80); + if((shift < 32) && (byte & 0x40)) + result |= -(1 << shift); + *bytesRead = count; + return result; +} + +u32 elfReadLEB128(u8 *data, int *bytesRead) +{ + u32 result = 0; + int shift = 0; + int count = 0; + u8 byte; + do + { + byte = *data++; + count++; + result |= (byte & 0x7f) << shift; + shift += 7; + } + while(byte & 0x80); + *bytesRead = count; + return result; +} + +u8 *elfReadSection(u8 *data, ELFSectionHeader *sh) +{ + return data + READ32LE(&sh->offset); +} + +ELFSectionHeader *elfGetSectionByName(char *name) +{ + for(int i = 0; i < elfSectionHeadersCount; i++) + { + if(strcmp(name, + &elfSectionHeadersStringTable[READ32LE(&elfSectionHeaders[i]-> + name)]) == 0) + { + return elfSectionHeaders[i]; + } + } + return NULL; +} + +ELFSectionHeader *elfGetSectionByNumber(int number) +{ + if(number < elfSectionHeadersCount) + { + return elfSectionHeaders[number]; + } + return NULL; +} + +CompileUnit *elfGetCompileUnitForData(u8 *data) +{ + u8 *end = elfCurrentUnit->top + 4 + elfCurrentUnit->length; + + if(data >= elfCurrentUnit->top && data < end) + return elfCurrentUnit; + + CompileUnit *unit = elfCompileUnits; + + while(unit) + { + end = unit->top + 4 + unit->length; + + if(data >= unit->top && data < end) + return unit; + + unit = unit->next; + } + + printf("Error: cannot find reference to compile unit at offset %08x\n", + (int)(data - elfDebugInfo->infodata)); + exit(-1); +} + +u8 *elfReadAttribute(u8 *data, ELFAttr *attr) +{ + int bytes; + int form = attr->form; +start: + switch(form) + { + case DW_FORM_addr: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_data2: + attr->value = elfRead2Bytes(data); + data += 2; + break; + case DW_FORM_data4: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_string: + attr->string = (char *)data; + data += strlen(attr->string)+1; + break; + case DW_FORM_strp: + attr->string = elfDebugStrings + elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_block: + attr->block = (ELFBlock *)malloc(sizeof(ELFBlock)); + attr->block->length = elfReadLEB128(data, &bytes); + data += bytes; + attr->block->data = data; + data += attr->block->length; + break; + case DW_FORM_block1: + attr->block = (ELFBlock *)malloc(sizeof(ELFBlock)); + attr->block->length = *data++; + attr->block->data = data; + data += attr->block->length; + break; + case DW_FORM_data1: + attr->value = *data++; + break; + case DW_FORM_flag: + attr->flag = (*data++) ? true : false; + break; + case DW_FORM_sdata: + attr->value = elfReadSignedLEB128(data, &bytes); + data += bytes; + break; + case DW_FORM_udata: + attr->value = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_FORM_ref_addr: + attr->value = (elfDebugInfo->infodata + elfRead4Bytes(data)) - + elfGetCompileUnitForData(data)->top; + data += 4; + break; + case DW_FORM_ref4: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_ref_udata: + attr->value = (elfDebugInfo->infodata + + (elfGetCompileUnitForData(data)->top - + elfDebugInfo->infodata) + + elfReadLEB128(data, &bytes)) - + elfCurrentUnit->top; + data += bytes; + break; + case DW_FORM_indirect: + form = elfReadLEB128(data, &bytes); + data += bytes; + goto start; + default: + fprintf(stderr, "Unsupported FORM %02x\n", form); + exit(-1); + } + return data; +} + +ELFAbbrev *elfGetAbbrev(ELFAbbrev **table, u32 number) +{ + int hash = number % 121; + + ELFAbbrev *abbrev = table[hash]; + + while(abbrev) + { + if(abbrev->number == number) + return abbrev; + abbrev = abbrev->next; + } + return NULL; +} + +ELFAbbrev **elfReadAbbrevs(u8 *data, u32 offset) +{ + data += offset; + ELFAbbrev **abbrevs = (ELFAbbrev **)calloc(sizeof(ELFAbbrev *)*121,1); + int bytes = 0; + u32 number = elfReadLEB128(data, &bytes); + data += bytes; + while(number) + { + ELFAbbrev *abbrev = (ELFAbbrev *)calloc(sizeof(ELFAbbrev),1); + + // read tag information + abbrev->number = number; + abbrev->tag = elfReadLEB128(data, &bytes); + data += bytes; + abbrev->hasChildren = *data++ ? true: false; + + // read attributes + int name = elfReadLEB128(data, &bytes); + data += bytes; + int form = elfReadLEB128(data, &bytes); + data += bytes; + + while(name) + { + if((abbrev->numAttrs % 4) == 0) + { + abbrev->attrs = (ELFAttr *)realloc(abbrev->attrs, + (abbrev->numAttrs + 4) * + sizeof(ELFAttr)); + } + abbrev->attrs[abbrev->numAttrs].name = name; + abbrev->attrs[abbrev->numAttrs++].form = form; + + name = elfReadLEB128(data, &bytes); + data += bytes; + form = elfReadLEB128(data, &bytes); + data += bytes; + } + + int hash = number % 121; + abbrev->next = abbrevs[hash]; + abbrevs[hash] = abbrev; + + number = elfReadLEB128(data, &bytes); + data += bytes; + + if(elfGetAbbrev(abbrevs, number) != NULL) + break; + } + + return abbrevs; +} + +void elfParseCFA(u8 *top) +{ + ELFSectionHeader *h = elfGetSectionByName(".debug_frame"); + + if(h == NULL) + { + return; + } + + u8 *data = elfReadSection(top, h); + + u8 *topOffset = data; + + u8 *end = data + READ32LE(&h->size); + + ELFcie *cies = NULL; + + while(data < end) + { + u32 offset = data - topOffset; + u32 len = elfRead4Bytes(data); + data += 4; + + u8 *dataEnd = data + len; + + u32 id = elfRead4Bytes(data); + data += 4; + + if(id == 0xffffffff) + { + // skip version + *data++; + + ELFcie *cie = (ELFcie *)calloc(1, sizeof(ELFcie)); + + cie->next = cies; + cies = cie; + + cie->offset = offset; + + cie->augmentation = data; + while(*data) + data++; + data++; + + if(*cie->augmentation) + { + fprintf(stderr, "Error: augmentation not supported\n"); + exit(-1); + } + + int bytes; + cie->codeAlign = elfReadLEB128(data, &bytes); + data += bytes; + + cie->dataAlign = elfReadSignedLEB128(data, &bytes); + data += bytes; + + cie->returnAddress = *data++; + + cie->data = data; + cie->dataLen = dataEnd - data; + } + else + { + ELFfde *fde = (ELFfde *)calloc(1, sizeof(ELFfde)); + + ELFcie *cie = cies; + + while(cie != NULL) + { + if(cie->offset == id) + break; + cie = cie->next; + } + + if(!cie) + { + fprintf(stderr, "Cannot find CIE %08x\n", id); + exit(-1); + } + + fde->cie = cie; + + fde->address = elfRead4Bytes(data); + data += 4; + + fde->end = fde->address + elfRead4Bytes(data); + data += 4; + + fde->data = data; + fde->dataLen = dataEnd - data; + + if((elfFdeCount %10) == 0) + { + elfFdes = (ELFfde **)realloc(elfFdes, (elfFdeCount+10) * + sizeof(ELFfde *)); + } + elfFdes[elfFdeCount++] = fde; + } + data = dataEnd; + } + + elfCies = cies; +} + +void elfAddLine(LineInfo *l, u32 a, int file, int line, int *max) +{ + if(l->number == *max) + { + *max += 1000; + l->lines = (LineInfoItem *)realloc(l->lines, *max*sizeof(LineInfoItem)); + } + LineInfoItem *li = &l->lines[l->number]; + li->file = l->files[file-1]; + li->address = a; + li->line = line; + l->number++; +} + +void elfParseLineInfo(CompileUnit *unit, u8 *top) +{ + ELFSectionHeader *h = elfGetSectionByName(".debug_line"); + if(h == NULL) + { + fprintf(stderr, "No line information found\n"); + return; + } + LineInfo *l = unit->lineInfoTable = (LineInfo *)calloc(1, sizeof(LineInfo)); + l->number = 0; + int max = 1000; + l->lines = (LineInfoItem *)malloc(1000*sizeof(LineInfoItem)); + + u8 *data = elfReadSection(top, h); + data += unit->lineInfo; + u32 totalLen = elfRead4Bytes(data); + data += 4; + u8 *end = data + totalLen; + // u16 version = elfRead2Bytes(data); + data += 2; + // u32 offset = elfRead4Bytes(data); + data += 4; + int minInstrSize = *data++; + int defaultIsStmt = *data++; + int lineBase = (s8)*data++; + int lineRange = *data++; + int opcodeBase = *data++; + u8 *stdOpLen = (u8 *)malloc(opcodeBase * sizeof(u8)); + stdOpLen[0] = 1; + int i; + for(i = 1; i < opcodeBase; i++) + stdOpLen[i] = *data++; + + free(stdOpLen);// todo + int bytes = 0; + + char *s; + while((s = elfReadString(data, &bytes)) != NULL) + { + data += bytes; + // fprintf(stderr, "Directory is %s\n", s); + } + data += bytes; + int count = 4; + int index = 0; + l->files = (char **)malloc(sizeof(char *)*count); + + while((s = elfReadString(data, &bytes)) != NULL) + { + l->files[index++] = s; + + data += bytes; + // directory + elfReadLEB128(data, &bytes); + data += bytes; + // time + elfReadLEB128(data, &bytes); + data += bytes; + // size + elfReadLEB128(data, &bytes); + data += bytes; + // fprintf(stderr, "File is %s\n", s); + if(index == count) + { + count += 4; + l->files = (char **)realloc(l->files, sizeof(char *)*count); + } + } + l->fileCount = index; + data += bytes; + + while(data < end) + { + u32 address = 0; + int file = 1; + int line = 1; + int col = 0; + int isStmt = defaultIsStmt; + int basicBlock = 0; + int endSeq = 0; + + while(!endSeq) + { + int op = *data++; + switch(op) + { + case DW_LNS_extended_op: + { + data++; + op = *data++; + switch(op) + { + case DW_LNE_end_sequence: + endSeq = 1; + break; + case DW_LNE_set_address: + address = elfRead4Bytes(data); + data += 4; + break; + default: + fprintf(stderr, "Unknown extended LINE opcode %02x\n", op); + exit(-1); + } + } + break; + case DW_LNS_copy: + // fprintf(stderr, "Address %08x line %d (%d)\n", address, line, file); + elfAddLine(l, address, file, line, &max); + basicBlock = 0; + break; + case DW_LNS_advance_pc: + address += minInstrSize * elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_advance_line: + line += elfReadSignedLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_set_file: + file = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_set_column: + col = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_negate_stmt: + isStmt = !isStmt; + break; + case DW_LNS_set_basic_block: + basicBlock = 1; + break; + case DW_LNS_const_add_pc: + address += (minInstrSize *((255 - opcodeBase)/lineRange)); + break; + case DW_LNS_fixed_advance_pc: + address += elfRead2Bytes(data); + data += 2; + break; + default: + op = op - opcodeBase; + address += (op / lineRange) * minInstrSize; + line += lineBase + (op % lineRange); + elfAddLine(l, address, file, line, &max); + // fprintf(stderr, "Address %08x line %d (%d)\n", address, line,file); + basicBlock = 1; + break; + } + } + } + l->lines = (LineInfoItem *)realloc(l->lines, l->number*sizeof(LineInfoItem)); +} + +u8 *elfSkipData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs) +{ + int i; + int bytes; + + for(i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) + { + int nesting = 1; + while(nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + for(i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) + { + nesting++; + } + } + } + return data; +} + +Type *elfParseType(CompileUnit *unit, u32); +u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Object **object); +u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function **function); +void elfCleanUp(Function *); + +void elfAddType(Type *type, CompileUnit *unit, u32 offset) +{ + if(type->next == NULL) + { + if(unit->types != type && type->offset == 0) + { + type->offset = offset; + type->next = unit->types; + unit->types = type; + } + } +} + +void elfParseType(u8 *data, u32 offset, ELFAbbrev *abbrev, CompileUnit *unit, + Type **type) +{ + switch(abbrev->tag) + { + case DW_TAG_typedef: + { + u32 typeref = 0; + char *name = NULL; + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_name: + name = attr->string; + break; + case DW_AT_type: + typeref = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + break; + default: + fprintf(stderr, "Unknown attribute for typedef %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for typedef\n"); + *type = elfParseType(unit, typeref); + if(name) + (*type)->name = name; + return; + } + break; + case DW_TAG_union_type: + case DW_TAG_structure_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + if(abbrev->tag == DW_TAG_structure_type) + t->type = TYPE_struct; + else + t->type = TYPE_union; + + Struct *s = (Struct *)calloc(sizeof(Struct), 1); + t->structure = s; + elfAddType(t, unit, offset); + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + case DW_AT_sibling: + case DW_AT_containing_type: // todo? + case DW_AT_declaration: + case DW_AT_specification: // TODO: + break; + default: + fprintf(stderr, "Unknown attribute for struct %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + int index = 0; + while(num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) + { + case DW_TAG_member: + { + if((index % 4) == 0) + s->members = (Member *)realloc(s->members, + sizeof(Member)*(index+4)); + Member *m = &s->members[index]; + m->location = NULL; + m->bitOffset = 0; + m->bitSize = 0; + m->byteSize = 0; + for(int i = 0; i < abbr->numAttrs; i++) + { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_name: + m->name = attr->string; + break; + case DW_AT_type: + m->type = elfParseType(unit, attr->value); + break; + case DW_AT_data_member_location: + m->location = attr->block; + break; + case DW_AT_byte_size: + m->byteSize = attr->value; + break; + case DW_AT_bit_offset: + m->bitOffset = attr->value; + break; + case DW_AT_bit_size: + m->bitSize = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + case DW_AT_accessibility: + case DW_AT_artificial: // todo? + break; + default: + fprintf(stderr, "Unknown member attribute %02x\n", + attr->name); + } + } + index++; + } + break; + case DW_TAG_subprogram: + { + Function *fnc = NULL; + data = elfParseFunction(data, abbr, unit, &fnc); + if(fnc != NULL) + { + if(unit->lastFunction) + unit->lastFunction->next = fnc; + else + unit->functions = fnc; + unit->lastFunction = fnc; + } + } + break; + case DW_TAG_inheritance: + // TODO: add support + data = elfSkipData(data, abbr, unit->abbrevs); + break; +CASE_TYPE_TAG: + // skip types... parsed only when used + data = elfSkipData(data, abbr, unit->abbrevs); + break; + case DW_TAG_variable: + data = elfSkipData(data, abbr, unit->abbrevs); + break; + default: + fprintf(stderr, "Unknown struct tag %02x %s\n", abbr->tag, t->name); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + s->memberCount = index; + } + *type = t; + return; + } + break; + case DW_TAG_base_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_base; + elfAddType(t, unit, offset); + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_encoding: + t->encoding = attr->value; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_bit_size: + t->bitSize = attr->value; + break; + default: + fprintf(stderr, "Unknown attribute for base type %02x\n", + attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for base type\n"); + *type = t; + return; + } + break; + case DW_TAG_pointer_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_pointer; + + elfAddType(t, unit, offset); + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data =elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_type: + t->pointer = elfParseType(unit, attr->value); + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + default: + fprintf(stderr, "Unknown pointer type attribute %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for pointer type\n"); + *type = t; + return; + } + break; + case DW_TAG_reference_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_reference; + + elfAddType(t, unit, offset); + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data =elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_type: + t->pointer = elfParseType(unit, attr->value); + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + default: + fprintf(stderr, "Unknown ref type attribute %02x\n", attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for ref type\n"); + *type = t; + return; + } + break; + case DW_TAG_volatile_type: + { + u32 typeref = 0; + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_type: + typeref = attr->value; + break; + default: + fprintf(stderr, "Unknown volatile attribute for type %02x\n", + attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for volatile type\n"); + *type = elfParseType(unit, typeref); + return; + } + break; + case DW_TAG_const_type: + { + u32 typeref = 0; + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_type: + typeref = attr->value; + break; + default: + fprintf(stderr, "Unknown const attribute for type %02x\n", + attr->name); + break; + } + } + if(abbrev->hasChildren) + fprintf(stderr, "Unexpected children for const type\n"); + *type = elfParseType(unit, typeref); + return; + } + break; + case DW_TAG_enumeration_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_enum; + Enum *e = (Enum *)calloc(sizeof(Enum), 1); + t->enumeration = e; + elfAddType(t, unit, offset); + int count = 0; + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_sibling: + case DW_AT_decl_file: + case DW_AT_decl_line: + break; + default: + fprintf(stderr, "Unknown enum attribute %02x\n", attr->name); + } + } + if(abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + while(num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) + { + case DW_TAG_enumerator: + { + count++; + e->members = (EnumMember *)realloc(e->members, + count*sizeof(EnumMember)); + EnumMember *m = &e->members[count-1]; + for(int i = 0; i < abbr->numAttrs; i++) + { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_name: + m->name = attr->string; + break; + case DW_AT_const_value: + m->value = attr->value; + break; + default: + fprintf(stderr, "Unknown sub param attribute %02x\n", + attr->name); + } + } + } + break; + default: + fprintf(stderr, "Unknown enum tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + } + e->count = count; + *type = t; + return; + } + break; + case DW_TAG_subroutine_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_function; + FunctionType *f = (FunctionType *)calloc(sizeof(FunctionType), 1); + t->function = f; + elfAddType(t, unit, offset); + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_prototyped: + case DW_AT_sibling: + break; + case DW_AT_type: + f->returnType = elfParseType(unit, attr->value); + break; + default: + fprintf(stderr, "Unknown subroutine attribute %02x\n", attr->name); + } + } + if(abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + Object *lastVar = NULL; + while(num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) + { + case DW_TAG_formal_parameter: + { + Object *o; + data = elfParseObject(data, abbr, unit, &o); + if(f->args) + lastVar->next = o; + else + f->args = o; + lastVar = o; + } + break; + case DW_TAG_unspecified_parameters: + // no use in the debugger yet + data = elfSkipData(data, abbr, unit->abbrevs); + break; +CASE_TYPE_TAG: + // skip types... parsed only when used + data = elfSkipData(data, abbr, unit->abbrevs); + break; + default: + fprintf(stderr, "Unknown subroutine tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + } + *type = t; + return; + } + break; + case DW_TAG_array_type: + { + u32 typeref = 0; + int i; + Array *array = (Array *)calloc(sizeof(Array), 1); + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_array; + elfAddType(t, unit, offset); + + for(i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_type: + typeref = attr->value; + array->type = elfParseType(unit, typeref); + break; + default: + fprintf(stderr, "Unknown array attribute %02x\n", attr->name); + } + } + if(abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + int index = 0; + int maxBounds = 0; + while(num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch(abbr->tag) + { + case DW_TAG_subrange_type: + { + if(maxBounds == index) + { + maxBounds += 4; + array->bounds = (int *)realloc(array->bounds, + sizeof(int)*maxBounds); + } + for(int i = 0; i < abbr->numAttrs; i++) + { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_upper_bound: + array->bounds[index] = attr->value+1; + break; + case DW_AT_type: // ignore + break; + default: + fprintf(stderr, "Unknown subrange attribute %02x\n", + attr->name); + } + } + index++; + } + break; + default: + fprintf(stderr, "Unknown array tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + array->maxBounds = index; + } + t->size = array->type->size; + for(i = 0; i < array->maxBounds; i++) + t->size *= array->bounds[i]; + t->array = array; + *type = t; + return; + } + break; + default: + fprintf(stderr, "Unknown type TAG %02x\n", abbrev->tag); + exit(-1); + } +} + +Type *elfParseType(CompileUnit *unit, u32 offset) +{ + Type *t = unit->types; + + while(t) + { + if(t->offset == offset) + return t; + t = t->next; + } + if(offset == 0) + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_void; + t->offset = 0; + elfAddType(t, unit, 0); + return t; + } + u8 *data = unit->top + offset; + int bytes; + int abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + Type *type = NULL; + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + elfParseType(data, offset, abbrev, unit, &type); + return type; +} + +void elfGetObjectAttributes(CompileUnit *unit, u32 offset, Object *o) +{ + u8 *data = unit->top + offset; + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) + { + return; + } + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_location: + o->location = attr->block; + break; + case DW_AT_name: + if(o->name == NULL) + o->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + o->name = attr->string; + break; + case DW_AT_decl_file: + o->file = attr->value; + break; + case DW_AT_decl_line: + o->line = attr->value; + break; + case DW_AT_type: + o->type = elfParseType(unit, attr->value); + break; + case DW_AT_external: + o->external = attr->flag; + break; + case DW_AT_const_value: + case DW_AT_abstract_origin: + case DW_AT_declaration: + case DW_AT_artificial: + // todo + break; + case DW_AT_specification: + // TODO: + break; + default: + fprintf(stderr, "Unknown object attribute %02x\n", attr->name); + break; + } + } +} + +u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Object **object) +{ + Object *o = (Object *)calloc(sizeof(Object), 1); + + o->next = NULL; + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_location: + o->location = attr->block; + break; + case DW_AT_name: + if(o->name == NULL) + o->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + o->name = attr->string; + break; + case DW_AT_decl_file: + o->file = attr->value; + break; + case DW_AT_decl_line: + o->line = attr->value; + break; + case DW_AT_type: + o->type = elfParseType(unit, attr->value); + break; + case DW_AT_external: + o->external = attr->flag; + break; + case DW_AT_abstract_origin: + elfGetObjectAttributes(unit, attr->value, o); + break; + case DW_AT_const_value: + case DW_AT_declaration: + case DW_AT_artificial: + break; + case DW_AT_specification: + // TODO: + break; + default: + fprintf(stderr, "Unknown object attribute %02x\n", attr->name); + break; + } + } + *object = o; + return data; +} + +u8 *elfParseBlock(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function *func, Object **lastVar) +{ + int bytes; + u32 start = func->lowPC; + u32 end = func->highPC; + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_low_pc: + start = attr->value; + break; + case DW_AT_high_pc: + end = attr->value; + break; + case DW_AT_ranges: // ignore for now + break; + default: + fprintf(stderr, "Unknown block attribute %02x\n", attr->name); + break; + } + } + + if(abbrev->hasChildren) + { + int nesting = 1; + + while(nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + switch(abbrev->tag) + { +CASE_TYPE_TAG: // types only parsed when used + case DW_TAG_label: // not needed + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_lexical_block: + data = elfParseBlock(data, abbrev, unit, func, lastVar); + break; + case DW_TAG_subprogram: + { + Function *f = NULL; + data = elfParseFunction(data, abbrev, unit, &f); + if(f != NULL) + { + if(unit->lastFunction) + unit->lastFunction->next = f; + else + unit->functions = f; + unit->lastFunction = f; + } + } + break; + case DW_TAG_variable: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if(o->startScope == 0) + o->startScope = start; + if(o->endScope == 0) + o->endScope = 0; + if(func->variables) + (*lastVar)->next = o; + else + func->variables = o; + *lastVar = o; + } + break; + case DW_TAG_inlined_subroutine: + // TODO: + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + default: + { + fprintf(stderr, "Unknown block TAG %02x\n", abbrev->tag); + data = elfSkipData(data, abbrev, unit->abbrevs); + } + break; + } + } + } + return data; +} + +void elfGetFunctionAttributes(CompileUnit *unit, u32 offset, Function *func) +{ + u8 *data = unit->top + offset; + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) + { + return; + } + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + + switch(attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_name: + if(func->name == NULL) + func->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + func->name = attr->string; + break; + case DW_AT_low_pc: + func->lowPC = attr->value; + break; + case DW_AT_high_pc: + func->highPC = attr->value; + break; + case DW_AT_decl_file: + func->file = attr->value; + break; + case DW_AT_decl_line: + func->line = attr->value; + break; + case DW_AT_external: + func->external = attr->flag; + break; + case DW_AT_frame_base: + func->frameBase = attr->block; + break; + case DW_AT_type: + func->returnType = elfParseType(unit, attr->value); + break; + case DW_AT_inline: + case DW_AT_specification: + case DW_AT_declaration: + case DW_AT_artificial: + case DW_AT_prototyped: + case DW_AT_proc_body: + case DW_AT_save_offset: + case DW_AT_user_2002: + case DW_AT_virtuality: + case DW_AT_containing_type: + case DW_AT_accessibility: + // todo; + break; + case DW_AT_vtable_elem_location: + free(attr->block); + break; + default: + fprintf(stderr, "Unknown function attribute %02x\n", attr->name); + break; + } + } + + return; +} + +u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function **f) +{ + Function *func = (Function *)calloc(sizeof(Function), 1); + *f = func; + + int bytes; + bool mangled = false; + bool declaration = false; + for(int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch(attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_name: + if(func->name == NULL) + func->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + func->name = attr->string; + mangled = true; + break; + case DW_AT_low_pc: + func->lowPC = attr->value; + break; + case DW_AT_high_pc: + func->highPC = attr->value; + break; + case DW_AT_prototyped: + break; + case DW_AT_decl_file: + func->file = attr->value; + break; + case DW_AT_decl_line: + func->line = attr->value; + break; + case DW_AT_external: + func->external = attr->flag; + break; + case DW_AT_frame_base: + func->frameBase = attr->block; + break; + case DW_AT_type: + func->returnType = elfParseType(unit, attr->value); + break; + case DW_AT_abstract_origin: + elfGetFunctionAttributes(unit, attr->value, func); + break; + case DW_AT_declaration: + declaration = attr->flag; + break; + case DW_AT_inline: + case DW_AT_specification: + case DW_AT_artificial: + case DW_AT_proc_body: + case DW_AT_save_offset: + case DW_AT_user_2002: + case DW_AT_virtuality: + case DW_AT_containing_type: + case DW_AT_accessibility: + // todo; + break; + case DW_AT_vtable_elem_location: + free(attr->block); + break; + default: + fprintf(stderr, "Unknown function attribute %02x\n", attr->name); + break; + } + } + + if(declaration) + { + elfCleanUp(func); + free(func); + *f = NULL; + + while(1) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) + { + return data; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + data = elfSkipData(data, abbrev, unit->abbrevs); + } + } + + if(abbrev->hasChildren) + { + int nesting = 1; + Object *lastParam = NULL; + Object *lastVar = NULL; + + while(nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + switch(abbrev->tag) + { +CASE_TYPE_TAG: // no need to parse types. only parsed when used + case DW_TAG_label: // not needed + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_subprogram: + { + Function *fnc=NULL; + data = elfParseFunction(data, abbrev, unit, &fnc); + if(fnc != NULL) + { + if(unit->lastFunction == NULL) + unit->functions = fnc; + else + unit->lastFunction->next = fnc; + unit->lastFunction = fnc; + } + } + break; + case DW_TAG_lexical_block: + { + data = elfParseBlock(data, abbrev, unit, func, &lastVar); + } + break; + case DW_TAG_formal_parameter: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if(func->parameters) + lastParam->next = o; + else + func->parameters = o; + lastParam = o; + } + break; + case DW_TAG_variable: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if(func->variables) + lastVar->next = o; + else + func->variables = o; + lastVar = o; + } + break; + case DW_TAG_unspecified_parameters: + case DW_TAG_inlined_subroutine: + { + // todo + for(int i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) + nesting++; + } + break; + default: + { + fprintf(stderr, "Unknown function TAG %02x\n", abbrev->tag); + data = elfSkipData(data, abbrev, unit->abbrevs); + } + break; + } + } + } + return data; +} + +u8 *elfParseUnknownData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs) +{ + int i; + int bytes; + // switch(abbrev->tag) { + // default: + fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag); + + for(i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) + { + int nesting = 1; + while(nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if(!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag); + + for(i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if(abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if(abbrev->hasChildren) + { + nesting++; + } + } + } + // } + return data; +} + +u8 *elfParseCompileUnitChildren(u8 *data, CompileUnit *unit) +{ + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + Object *lastObj = NULL; + while(abbrevNum) + { + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + switch(abbrev->tag) + { + case DW_TAG_subprogram: + { + Function *func = NULL; + data = elfParseFunction(data, abbrev, unit, &func); + if(func != NULL) + { + if(unit->lastFunction) + unit->lastFunction->next = func; + else + unit->functions = func; + unit->lastFunction = func; + } + } + break; +CASE_TYPE_TAG: + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_variable: + { + Object *var = NULL; + data = elfParseObject(data, abbrev, unit, &var); + if(lastObj) + lastObj->next = var; + else + unit->variables = var; + lastObj = var; + } + break; + default: + data = elfParseUnknownData(data, abbrev, unit->abbrevs); + break; + } + + abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + } + return data; +} + + +CompileUnit *elfParseCompUnit(u8 *data, u8 *abbrevData) +{ + int bytes; + u8 *top = data; + + u32 length = elfRead4Bytes(data); + data += 4; + + u16 version = elfRead2Bytes(data); + data += 2; + + u32 offset = elfRead4Bytes(data); + data += 4; + + u8 addrSize = *data++; + + if(version != 2) + { + fprintf(stderr, "Unsupported debugging information version %d\n", version); + return NULL; + } + + if(addrSize != 4) + { + fprintf(stderr, "Unsupported address size %d\n", addrSize); + return NULL; + } + + ELFAbbrev **abbrevs = elfReadAbbrevs(abbrevData, offset); + + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + ELFAbbrev *abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + CompileUnit *unit = (CompileUnit *)calloc(sizeof(CompileUnit), 1); + unit->top = top; + unit->length = length; + unit->abbrevs = abbrevs; + unit->next = NULL; + + elfCurrentUnit = unit; + + int i; + + for(i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + + switch(attr->name) + { + case DW_AT_name: + unit->name = attr->string; + break; + case DW_AT_stmt_list: + unit->hasLineInfo = true; + unit->lineInfo = attr->value; + break; + case DW_AT_low_pc: + unit->lowPC = attr->value; + break; + case DW_AT_high_pc: + unit->highPC = attr->value; + break; + case DW_AT_compdir: + unit->compdir = attr->string; + break; + // ignore + case DW_AT_language: + case DW_AT_producer: + case DW_AT_macro_info: + case DW_AT_entry_pc: + break; + default: + fprintf(stderr, "Unknown attribute %02x\n", attr->name); + break; + } + } + + if(abbrev->hasChildren) + elfParseCompileUnitChildren(data, unit); + + return unit; +} + +void elfParseAranges(u8 *data) +{ + ELFSectionHeader *sh = elfGetSectionByName(".debug_aranges"); + if(sh == NULL) + { + fprintf(stderr, "No aranges found\n"); + return; + } + + data = elfReadSection(data, sh); + u8 *end = data + READ32LE(&sh->size); + + int max = 4; + ARanges *ranges = (ARanges *)calloc(sizeof(ARanges), 4); + + int index = 0; + + while(data < end) + { + u32 len = elfRead4Bytes(data); + data += 4; + // u16 version = elfRead2Bytes(data); + data += 2; + u32 offset = elfRead4Bytes(data); + data += 4; + // u8 addrSize = *data++; + // u8 segSize = *data++; + data += 2; // remove if uncommenting above + data += 4; + ranges[index].count = (len-20)/8; + ranges[index].offset = offset; + ranges[index].ranges = (ARange *)calloc(sizeof(ARange), (len-20)/8); + int i = 0; + while(true) + { + u32 addr = elfRead4Bytes(data); + data += 4; + u32 len = elfRead4Bytes(data); + data += 4; + if(addr == 0 && len == 0) + break; + ranges[index].ranges[i].lowPC = addr; + ranges[index].ranges[i].highPC = addr+len; + i++; + } + index++; + if(index == max) + { + max += 4; + ranges = (ARanges *)realloc(ranges, max*sizeof(ARanges)); + } + } + elfDebugInfo->numRanges = index; + elfDebugInfo->ranges = ranges; +} + +void elfReadSymtab(u8 *data) +{ + ELFSectionHeader *sh = elfGetSectionByName(".symtab"); + int table = READ32LE(&sh->link); + + char *strtable = (char *)elfReadSection(data, elfGetSectionByNumber(table)); + + ELFSymbol *symtab = (ELFSymbol *)elfReadSection(data, sh); + + int count = READ32LE(&sh->size) / sizeof(ELFSymbol); + elfSymbolsCount = 0; + + elfSymbols = (Symbol *)malloc(sizeof(Symbol)*count); + + int i; + + for(i = 0; i < count; i++) + { + ELFSymbol *s = &symtab[i]; + int type = s->info & 15; + int binding = s->info >> 4; + + if(binding) + { + Symbol *sym = &elfSymbols[elfSymbolsCount]; + sym->name = &strtable[READ32LE(&s->name)]; + sym->binding = binding; + sym->type = type; + sym->value = READ32LE(&s->value); + sym->size = READ32LE(&s->size); + elfSymbolsCount++; + } + } + for(i = 0; i < count; i++) + { + ELFSymbol *s = &symtab[i]; + int bind = s->info>>4; + int type = s->info & 15; + + if(!bind) + { + Symbol *sym = &elfSymbols[elfSymbolsCount]; + sym->name = &strtable[READ32LE(&s->name)]; + sym->binding = (s->info >> 4); + sym->type = type; + sym->value = READ32LE(&s->value); + sym->size = READ32LE(&s->size); + elfSymbolsCount++; + } + } + elfSymbolsStrTab = strtable; + // free(symtab); +} + +bool elfReadProgram(ELFHeader *eh, u8 *data, int& size, bool parseDebug) +{ + int count = READ16LE(&eh->e_phnum); + int i; + + if(READ32LE(&eh->e_entry) == 0x2000000) + cpuIsMultiBoot = true; + + // read program headers... should probably move this code down + u8 *p = data + READ32LE(&eh->e_phoff); + size = 0; + for(i = 0; i < count; i++) + { + ELFProgramHeader *ph = (ELFProgramHeader *)p; + p += sizeof(ELFProgramHeader); + if(READ16LE(&eh->e_phentsize) != sizeof(ELFProgramHeader)) + { + p += READ16LE(&eh->e_phentsize) - sizeof(ELFProgramHeader); + } + + // printf("PH %d %08x %08x %08x %08x %08x %08x %08x %08x\n", + // i, ph->type, ph->offset, ph->vaddr, ph->paddr, + // ph->filesz, ph->memsz, ph->flags, ph->align); + if(cpuIsMultiBoot) + { + if(READ32LE(&ph->paddr) >= 0x2000000 && + READ32LE(&ph->paddr) <= 0x203ffff) + { + memcpy(&workRAM[READ32LE(&ph->paddr) & 0x3ffff], + data + READ32LE(&ph->offset), + READ32LE(&ph->filesz)); + } + } + else + { + if(READ32LE(&ph->paddr) >= 0x8000000 && + READ32LE(&ph->paddr) <= 0x9ffffff) + { + memcpy(&rom[READ32LE(&ph->paddr) & 0x1ffffff], + data + READ32LE(&ph->offset), + READ32LE(&ph->filesz)); + size += READ32LE(&ph->filesz); + } + } + } + + char *stringTable = NULL; + + // read section headers + p = data + READ32LE(&eh->e_shoff); + count = READ16LE(&eh->e_shnum); + + ELFSectionHeader **sh = (ELFSectionHeader **) + malloc(sizeof(ELFSectionHeader *) * count); + + for(i = 0; i < count; i++) + { + sh[i] = (ELFSectionHeader *)p; + p += sizeof(ELFSectionHeader); + if(READ16LE(&eh->e_shentsize) != sizeof(ELFSectionHeader)) + p += READ16LE(&eh->e_shentsize) - sizeof(ELFSectionHeader); + } + + if(READ16LE(&eh->e_shstrndx) != 0) + { + stringTable = (char *)elfReadSection(data, + sh[READ16LE(&eh->e_shstrndx)]); + } + + elfSectionHeaders = sh; + elfSectionHeadersStringTable = stringTable; + elfSectionHeadersCount = count; + + for(i = 0; i < count; i++) + { + // printf("SH %d %-20s %08x %08x %08x %08x %08x %08x %08x %08x\n", + // i, &stringTable[sh[i]->name], sh[i]->name, sh[i]->type, + // sh[i]->flags, sh[i]->addr, sh[i]->offset, sh[i]->size, + // sh[i]->link, sh[i]->info); + if(READ32LE(&sh[i]->flags) & 2) + { // load section + if(cpuIsMultiBoot) + { + if(READ32LE(&sh[i]->addr) >= 0x2000000 && + READ32LE(&sh[i]->addr) <= 0x203ffff) + { + memcpy(&workRAM[READ32LE(&sh[i]->addr) & 0x3ffff], data + + READ32LE(&sh[i]->offset), + READ32LE(&sh[i]->size)); + } + } + else + { + if(READ32LE(&sh[i]->addr) >= 0x8000000 && + READ32LE(&sh[i]->addr) <= 0x9ffffff) + { + memcpy(&rom[READ32LE(&sh[i]->addr) & 0x1ffffff], + data + READ32LE(&sh[i]->offset), + READ32LE(&sh[i]->size)); + size += READ32LE(&sh[i]->size); + } + } + } + } + + if(parseDebug) + { + fprintf(stderr, "Parsing debug info\n"); + + ELFSectionHeader *dbgHeader = elfGetSectionByName(".debug_info"); + if(dbgHeader == NULL) + { + fprintf(stderr, "Cannot find debug information\n"); + goto end; + } + + ELFSectionHeader *h = elfGetSectionByName(".debug_abbrev"); + if(h == NULL) + { + fprintf(stderr, "Cannot find abbreviation table\n"); + goto end; + } + + elfDebugInfo = (DebugInfo *)calloc(sizeof(DebugInfo), 1); + u8 *abbrevdata = elfReadSection(data, h); + + h = elfGetSectionByName(".debug_str"); + + if(h == NULL) + elfDebugStrings = NULL; + else + elfDebugStrings = (char *)elfReadSection(data, h); + + u8 *debugdata = elfReadSection(data, dbgHeader); + + elfDebugInfo->debugdata = data; + elfDebugInfo->infodata = debugdata; + + u32 total = READ32LE(&dbgHeader->size); + u8 *end = debugdata + total; + u8 *ddata = debugdata; + + CompileUnit *last = NULL; + CompileUnit *unit = NULL; + + while(ddata < end) + { + unit = elfParseCompUnit(ddata, abbrevdata); + unit->offset = ddata-debugdata; + elfParseLineInfo(unit, data); + if(last == NULL) + elfCompileUnits = unit; + else + last->next = unit; + last = unit; + ddata += 4 + unit->length; + } + elfParseAranges(data); + CompileUnit *comp = elfCompileUnits; + while(comp) + { + ARanges *r = elfDebugInfo->ranges; + for(int i = 0; i < elfDebugInfo->numRanges; i++) + if(r[i].offset == comp->offset) + { + comp->ranges = &r[i]; + break; + } + comp = comp->next; + } + elfParseCFA(data); + elfReadSymtab(data); + } +end: + if(sh) + { + free(sh); + } + + elfSectionHeaders = NULL; + elfSectionHeadersStringTable = NULL; + elfSectionHeadersCount = 0; + + return true; +} + +extern bool parseDebug; + +bool elfRead(const char *name, int& siz, FILE* f) +{ + + fseek(f, 0, SEEK_END); + long size = ftell(f); + + elfFileData = (u8 *)malloc(size); + gen_fseek(f, 0, SEEK_SET); + gen_fread(elfFileData, 1, size, f); + gen_fclose(f); + + ELFHeader *header = (ELFHeader *)elfFileData; + + if(READ32LE(&header->magic) != 0x464C457F || + READ16LE(&header->e_machine) != 40 || + header->clazz != 1) + { + systemMessage(0, N_("Not a valid ELF file %s"), name); + free(elfFileData); + elfFileData = NULL; + return false; + } + + if(!elfReadProgram(header, elfFileData, siz, parseDebug)) + { + free(elfFileData); + elfFileData = NULL; + return false; + } + + return true; +} + +void elfCleanUp(Object *o) +{ + free(o->location); +} + +void elfCleanUp(Function *func) +{ + Object *o = func->parameters; + while(o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + + o = func->variables; + while(o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + free(func->frameBase); +} + +void elfCleanUp(ELFAbbrev **abbrevs) +{ + for(int i = 0; i < 121; i++) + { + ELFAbbrev *abbrev = abbrevs[i]; + + while(abbrev) + { + free(abbrev->attrs); + ELFAbbrev *next = abbrev->next; + free(abbrev); + + abbrev = next; + } + } +} + +void elfCleanUp(Type *t) +{ + switch(t->type) + { + case TYPE_function: + if(t->function) + { + Object *o = t->function->args; + while(o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + free(t->function); + } + break; + case TYPE_array: + if(t->array) + { + free(t->array->bounds); + free(t->array); + } + break; + case TYPE_struct: + case TYPE_union: + if(t->structure) + { + for(int i = 0; i < t->structure->memberCount; i++) + { + free(t->structure->members[i].location); + } + free(t->structure->members); + free(t->structure); + } + break; + case TYPE_enum: + if(t->enumeration) + { + free(t->enumeration->members); + free(t->enumeration); + } + break; + case TYPE_base: + case TYPE_pointer: + case TYPE_void: + case TYPE_reference: + break; // nothing to do + } +} + +void elfCleanUp(CompileUnit *comp) +{ + elfCleanUp(comp->abbrevs); + free(comp->abbrevs); + Function *func = comp->functions; + while(func) + { + elfCleanUp(func); + Function *next = func->next; + free(func); + func = next; + } + Type *t = comp->types; + while(t) + { + elfCleanUp(t); + Type *next = t->next; + free(t); + t = next; + } + Object *o = comp->variables; + while(o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + if(comp->lineInfoTable) + { + free(comp->lineInfoTable->lines); + free(comp->lineInfoTable->files); + free(comp->lineInfoTable); + } +} + +void elfCleanUp() +{ + CompileUnit *comp = elfCompileUnits; + + while(comp) + { + elfCleanUp(comp); + CompileUnit *next = comp->next; + free(comp); + comp = next; + } + elfCompileUnits = NULL; + free(elfSymbols); + elfSymbols = NULL; + // free(elfSymbolsStrTab); + elfSymbolsStrTab = NULL; + + elfDebugStrings = NULL; + if(elfDebugInfo) + { + int num = elfDebugInfo->numRanges; + int i; + for(i = 0; i < num; i++) + { + free(elfDebugInfo->ranges[i].ranges); + } + free(elfDebugInfo->ranges); + free(elfDebugInfo); + elfDebugInfo = NULL; + } + + if(elfFdes) + { + if(elfFdeCount) + { + for(int i = 0; i < elfFdeCount; i++) + free(elfFdes[i]); + } + free(elfFdes); + + elfFdes = NULL; + elfFdeCount = 0; + } + + ELFcie *cie = elfCies; + while(cie) + { + ELFcie *next = cie->next; + free(cie); + cie = next; + } + elfCies = NULL; + + if(elfFileData) + { + free(elfFileData); + elfFileData = NULL; + } +} diff --git a/src/elf.h b/src/elf.h new file mode 100644 index 0000000..b2e070a --- /dev/null +++ b/src/elf.h @@ -0,0 +1,311 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_ELF_H +#define VBA_ELF_H +#include +#include +#include +#include +#include + +enum LocationType { + LOCATION_register, + LOCATION_memory, + LOCATION_value +}; + +#define DW_ATE_boolean 0x02 +#define DW_ATE_signed 0x05 +#define DW_ATE_unsigned 0x07 +#define DW_ATE_unsigned_char 0x08 + +struct ELFHeader + { + u32 magic; + u8 clazz; + u8 data; + u8 version; + u8 pad[9]; + u16 e_type; + u16 e_machine; + u32 e_version; + u32 e_entry; + u32 e_phoff; + u32 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shstrndx; + }; + +struct ELFProgramHeader + { + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; + }; + +struct ELFSectionHeader + { + u32 name; + u32 type; + u32 flags; + u32 addr; + u32 offset; + u32 size; + u32 link; + u32 info; + u32 addralign; + u32 entsize; + }; + +struct ELFSymbol + { + u32 name; + u32 value; + u32 size; + u8 info; + u8 other; + u16 shndx; + }; + +struct ELFBlock + { + int length; + u8 *data; + }; + +struct ELFAttr + { + u32 name; + u32 form; + union { + u32 value; + char *string; + u8 *data; + bool flag; + ELFBlock *block; + }; + }; + +struct ELFAbbrev + { + u32 number; + u32 tag; + bool hasChildren; + int numAttrs; + ELFAttr *attrs; + ELFAbbrev *next; + }; + +enum TypeEnum { + TYPE_base, + TYPE_pointer, + TYPE_function, + TYPE_void, + TYPE_array, + TYPE_struct, + TYPE_reference, + TYPE_enum, + TYPE_union +}; + +struct Type; +struct Object; + +struct FunctionType + { + Type *returnType; + Object *args; + }; + +struct Member + { + char *name; + Type *type; + int bitSize; + int bitOffset; + int byteSize; + ELFBlock *location; + }; + +struct Struct + { + int memberCount; + Member *members; + }; + +struct Array + { + Type *type; + int maxBounds; + int *bounds; + }; + +struct EnumMember + { + char *name; + u32 value; + }; + +struct Enum + { + int count; + EnumMember *members; + }; + +struct Type + { + u32 offset; + TypeEnum type; + char *name; + int encoding; + int size; + int bitSize; + union { + Type *pointer; + FunctionType *function; + Array *array; + Struct *structure; + Enum *enumeration; + }; + Type *next; + }; + +struct Object + { + char *name; + int file; + int line; + bool external; + Type *type; + ELFBlock *location; + u32 startScope; + u32 endScope; + Object *next; + }; + +struct Function + { + char *name; + u32 lowPC; + u32 highPC; + int file; + int line; + bool external; + Type *returnType; + Object *parameters; + Object *variables; + ELFBlock *frameBase; + Function *next; + }; + +struct LineInfoItem + { + u32 address; + char *file; + int line; + }; + +struct LineInfo + { + int fileCount; + char **files; + int number; + LineInfoItem *lines; + }; + +struct ARange + { + u32 lowPC; + u32 highPC; + }; + +struct ARanges + { + u32 offset; + int count; + ARange *ranges; + }; + +struct CompileUnit + { + u32 length; + u8 *top; + u32 offset; + ELFAbbrev **abbrevs; + ARanges *ranges; + char *name; + char *compdir; + u32 lowPC; + u32 highPC; + bool hasLineInfo; + u32 lineInfo; + LineInfo *lineInfoTable; + Function *functions; + Function *lastFunction; + Object *variables; + Type *types; + CompileUnit *next; + }; + +struct DebugInfo + { + u8 *debugfile; + u8 *abbrevdata; + u8 *debugdata; + u8 *infodata; + int numRanges; + ARanges *ranges; + }; + +struct Symbol + { + char *name; + int type; + int binding; + u32 address; + u32 value; + u32 size; + }; + +extern u32 elfReadLEB128(u8 *, int *); +extern s32 elfReadSignedLEB128(u8 *, int *); +extern bool elfRead(const char *, int &, FILE* f); +extern bool elfGetSymbolAddress(char *,u32 *, u32 *, int *); +extern char *elfGetAddressSymbol(u32); +extern char *elfGetSymbol(int, u32 *, u32 *, int *); +extern void elfCleanUp(); +extern bool elfGetCurrentFunction(u32, Function **, CompileUnit **c); +extern bool elfGetObject(char *, Function *, CompileUnit *, Object **); +extern bool elfFindLineInUnit(u32 *, CompileUnit *, int); +extern bool elfFindLineInModule(u32 *, char *, int); +u32 elfDecodeLocation(Function *, ELFBlock *, LocationType *); +u32 elfDecodeLocation(Function *, ELFBlock *, LocationType *, u32); +int elfFindLine(CompileUnit *unit, Function *func, u32 addr, char **); +#endif diff --git a/src/exprNode.h b/src/exprNode.h new file mode 100644 index 0000000..f32614d --- /dev/null +++ b/src/exprNode.h @@ -0,0 +1,69 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +struct Node + { + Type *type; + u32 location; + u32 objLocation; + LocationType locType; + int value; + int index; + char *name; + Node *expression; + Member *member; + void (*print)(Node *); + bool (*resolve)(Node *, Function *f, CompileUnit *u); + }; + +extern void exprNodeCleanUp(); + +extern Node *exprNodeIdentifier(); +extern void exprNodeIdentifierPrint(Node *); +extern bool exprNodeIdentifierResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeNumber(); +extern void exprNodeNumberPrint(Node *); +extern bool exprNodeNumberResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeStar(Node *); +extern void exprNodeStarPrint(Node *); +extern bool exprNodeStarResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeDot(Node *, Node *); +extern void exprNodeDotPrint(Node *); +extern bool exprNodeDotResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeArrow(Node *, Node *); +extern void exprNodeArrowPrint(Node *); +extern bool exprNodeArrowResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeAddr(Node *); +extern void exprNodeAddrPrint(Node *); +extern bool exprNodeAddrResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeSizeof(Node *); +extern void exprNodeSizeofPrint(Node *); +extern bool exprNodeSizeofResolve(Node *, Function *, CompileUnit *); + +extern Node *exprNodeArray(Node *, Node *); +extern void exprNodeArrayPrint(Node *); +extern bool exprNodeArrayResolve(Node *, Function *, CompileUnit *); + +#define YYSTYPE struct Node * diff --git a/src/fileio/sdfileio.c b/src/fileio/sdfileio.c new file mode 100644 index 0000000..058467e --- /dev/null +++ b/src/fileio/sdfileio.c @@ -0,0 +1,110 @@ +/**************************************************************************** +* Generic File I/O for VisualBoyAdvance +* +* Currently only supports SD +****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#define MAXDIRENTRIES 1000 +char direntries[MAXDIRENTRIES][255]; + +/** + * SDInit + */ +void SDInit( void ) +{ + fatInitDefault(); +} + +/** + * SD Card f_open + */ +FILE* gen_fopen( const char *filename, const char *mode ) +{ + return fopen( filename, mode ); +} + +/** + * SD Card f_write + */ +int gen_fwrite( const void *buffer, int len, int block, FILE* f ) +{ + return fwrite(buffer, len, block, f); +} + +/** + * SD Card f_read + */ +int gen_fread( void *buffer, int len, int block, FILE* f ) +{ + + return fread(buffer, len, block, f); +} + +/** + * SD Card fclose + */ +void gen_fclose( FILE* f ) +{ + fclose(f); +} + +/** + * SD Card fseek + * + * NB: Only supports SEEK_SET + */ +int gen_fseek(FILE* f, int where, int whence) +{ + fseek(f, where, whence); + return 1; +} + +/** + * Simple fgetc + */ +int gen_fgetc( FILE* f ) +{ + return fgetc(f); +} + +static struct stat _fstat; +char filename[1024]; +int fcount = 0; + +/** + * Get directory listing + */ +int gen_getdir( char *thisdir ) +{ + memset(&direntries[0],0,MAXDIRENTRIES*255); + + DIR_ITER* dp = diropen( thisdir ); + + if ( dp ) + { + while ( dirnext(dp, filename, &_fstat) == 0 ) + { + + // Skip any sub directories + if ( !(_fstat.st_mode & S_IFDIR) ) + { + memcpy(&direntries[fcount],&filename,strlen(filename)); + fcount++; + } + } + dirclose(dp); + } + else + return 0; + + + return fcount; + +} + diff --git a/src/fileio/sdfileio.h b/src/fileio/sdfileio.h new file mode 100644 index 0000000..be84d5d --- /dev/null +++ b/src/fileio/sdfileio.h @@ -0,0 +1,33 @@ +/**************************************************************************** +* Generic File I/O for VisualBoyAdvance +* +* Currently only supports SD +****************************************************************************/ +#ifndef __SDFILEIO__ +#define __SDFILEIO__ + + +#define MAXDIRENTRIES 1000 +#include +#include +#include +#include +#include + +extern "C" + { + + /* Required Functions */ + FILE* gen_fopen( const char *filename, const char *mode ); + int gen_fwrite( const void *buffer, int len, int block, FILE* f ); + int gen_fread( void *buffer, int len, int block, FILE* f ); + void gen_fclose( FILE* f ); + int gen_fseek(FILE* f, int where, int whence); + int gen_fgetc( FILE* f ); + int SDInit( void ); + int gen_getdir( char *thisdir ); + extern char direntries[MAXDIRENTRIES][255]; + } + +#endif + diff --git a/src/gb/GB.cpp b/src/gb/GB.cpp new file mode 100644 index 0000000..2edf6fd --- /dev/null +++ b/src/gb/GB.cpp @@ -0,0 +1,3468 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "sdfileio.h" +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "GB.h" +#include "gbCheats.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gbSGB.h" +#include "gbSound.h" +#include "../unzip.h" +#include "../Util.h" + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +extern u8 *pix; +extern bool speedup; + +bool gbUpdateSizes(); + +// debugging +bool memorydebug = false; +char gbBuffer[2048]; + +extern u16 gbLineMix[160]; + +// mappers +void (*mapper)(u16,u8) = NULL; +void (*mapperRAM)(u16,u8) = NULL; +u8 (*mapperReadRAM)(u16) = NULL; + +// registers +gbRegister PC; +gbRegister SP; +gbRegister AF; +gbRegister BC; +gbRegister DE; +gbRegister HL; +u16 IFF; +// 0xff04 +u8 register_DIV = 0; +// 0xff05 +u8 register_TIMA = 0; +// 0xff06 +u8 register_TMA = 0; +// 0xff07 +u8 register_TAC = 0; +// 0xff0f +u8 register_IF = 0; +// 0xff40 +u8 register_LCDC = 0; +// 0xff41 +u8 register_STAT = 0; +// 0xff42 +u8 register_SCY = 0; +// 0xff43 +u8 register_SCX = 0; +// 0xff44 +u8 register_LY = 0; +// 0xff45 +u8 register_LYC = 0; +// 0xff46 +u8 register_DMA = 0; +// 0xff4a +u8 register_WY = 0; +// 0xff4b +u8 register_WX = 0; +// 0xff4f +u8 register_VBK = 0; +// 0xff51 +u8 register_HDMA1 = 0; +// 0xff52 +u8 register_HDMA2 = 0; +// 0xff53 +u8 register_HDMA3 = 0; +// 0xff54 +u8 register_HDMA4 = 0; +// 0xff55 +u8 register_HDMA5 = 0; +// 0xff70 +u8 register_SVBK = 0; +// 0xffff +u8 register_IE = 0; + +// ticks definition +int GBDIV_CLOCK_TICKS = 64; +int GBLCD_MODE_0_CLOCK_TICKS = 51; +int GBLCD_MODE_1_CLOCK_TICKS = 1140; +int GBLCD_MODE_2_CLOCK_TICKS = 20; +int GBLCD_MODE_3_CLOCK_TICKS = 43; +int GBLY_INCREMENT_CLOCK_TICKS = 114; +int GBTIMER_MODE_0_CLOCK_TICKS = 256; +int GBTIMER_MODE_1_CLOCK_TICKS = 4; +int GBTIMER_MODE_2_CLOCK_TICKS = 16; +int GBTIMER_MODE_3_CLOCK_TICKS = 64; +int GBSERIAL_CLOCK_TICKS = 128; +int GBSYNCHRONIZE_CLOCK_TICKS = 52920; + +// state variables + +// interrupt +int gbInterrupt = 0; +int gbInterruptWait = 0; +// serial +int gbSerialOn = 0; +int gbSerialTicks = 0; +int gbSerialBits = 0; +// timer +int gbTimerOn = 0; +int gbTimerTicks = 0; +int gbTimerClockTicks = 0; +int gbTimerMode = 0; +// lcd +int gbLcdMode = 2; +int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS; +int gbLcdLYIncrementTicks = 0; +// div +int gbDivTicks = GBDIV_CLOCK_TICKS; +// cgb +int gbVramBank = 0; +int gbWramBank = 1; +int gbHdmaSource = 0x0000; +int gbHdmaDestination = 0x8000; +int gbHdmaBytes = 0x0000; +int gbHdmaOn = 0; +int gbSpeed = 0; +// frame counting +int gbFrameCount = 0; +int gbFrameSkip = 0; +int gbFrameSkipCount = 0; +// timing +u32 gbLastTime = 0; +u32 gbElapsedTime = 0; +u32 gbTimeNow = 0; +int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; +// emulator features +int gbBattery = 0; +int gbCaptureNumber = 0; +bool gbCapture = false; +bool gbCapturePrevious = false; +int gbJoymask[4] = { 0, 0, 0, 0 }; + +int gbRomSizes[] = { 0x00008000, // 32K + 0x00010000, // 64K + 0x00020000, // 128K + 0x00040000, // 256K + 0x00080000, // 512K + 0x00100000, // 1024K + 0x00200000, // 2048K + 0x00400000, // 4096K + 0x00800000 // 8192K + }; +int gbRomSizesMasks[] = { 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff + }; + +int gbRamSizes[6] = { 0x00000000, // 0K + 0x00000800, // 2K + 0x00002000, // 8K + 0x00008000, // 32K + 0x00020000, // 128K + 0x00010000 // 64K + }; + +int gbRamSizesMasks[6] = { 0x00000000, + 0x000007ff, + 0x00001fff, + 0x00007fff, + 0x0001ffff, + 0x0000ffff + }; + +int gbCycles[] = { + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 + 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 + 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 + 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 + 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b + 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c + 2, 3, 3, 0, 3, 4, 2, 4, 2, 4, 3, 0, 3, 0, 2, 4, // d + 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4, // e + 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4 // f + }; + +int gbCyclesCB[] = { + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 8 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 9 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // a + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // b + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // c + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // d + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // e + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2 // f + }; + +u16 DAATable[] = { + 0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0800,0x0900,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1800,0x1900,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, + 0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, + 0x2800,0x2900,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, + 0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700, + 0x3800,0x3900,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4800,0x4900,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5800,0x5900,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, + 0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700, + 0x6800,0x6900,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, + 0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700, + 0x7800,0x7900,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, + 0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700, + 0x8800,0x8900,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, + 0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700, + 0x9800,0x9900,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710, + 0x6810,0x6910,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, + 0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710, + 0x7810,0x7910,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, + 0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710, + 0x8810,0x8910,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, + 0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710, + 0x9810,0x9910,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, + 0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710, + 0xA810,0xA910,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, + 0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710, + 0xB810,0xB910,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, + 0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710, + 0xC810,0xC910,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, + 0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710, + 0xD810,0xD910,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, + 0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710, + 0xE810,0xE910,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, + 0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710, + 0xF810,0xF910,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00, + 0x0E00,0x0F00,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, + 0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00, + 0x1E00,0x1F00,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, + 0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00, + 0x2E00,0x2F00,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, + 0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00, + 0x3E00,0x3F00,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, + 0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00, + 0x4E00,0x4F00,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, + 0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00, + 0x5E00,0x5F00,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, + 0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00, + 0x6E00,0x6F00,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, + 0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00, + 0x7E00,0x7F00,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, + 0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00, + 0x8E00,0x8F00,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, + 0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00, + 0x9E00,0x9F00,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10, + 0x6E10,0x6F10,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, + 0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10, + 0x7E10,0x7F10,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, + 0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10, + 0x8E10,0x8F10,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, + 0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10, + 0x9E10,0x9F10,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, + 0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10, + 0xAE10,0xAF10,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, + 0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10, + 0xBE10,0xBF10,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, + 0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10, + 0xCE10,0xCF10,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, + 0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10, + 0xDE10,0xDF10,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, + 0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10, + 0xEE10,0xEF10,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, + 0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10, + 0xFE10,0xFF10,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740, + 0x0840,0x0940,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740, + 0x1840,0x1940,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740, + 0x2840,0x2940,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740, + 0x3840,0x3940,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740, + 0x4840,0x4940,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740, + 0x5840,0x5940,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740, + 0x6840,0x6940,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740, + 0x7840,0x7940,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740, + 0x8840,0x8940,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740, + 0x9840,0x9940,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750, + 0xA850,0xA950,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750, + 0xB850,0xB950,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750, + 0xC850,0xC950,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750, + 0xD850,0xD950,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750, + 0xE850,0xE950,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750, + 0xF850,0xF950,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750, + 0x0850,0x0950,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750, + 0x1850,0x1950,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750, + 0x2850,0x2950,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750, + 0x3850,0x3950,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0xFA60,0xFB60,0xFC60,0xFD60,0xFE60,0xFF60,0x00C0,0x0140, + 0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x0A60,0x0B60,0x0C60,0x0D60,0x0E60,0x0F60,0x1040,0x1140, + 0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x1A60,0x1B60,0x1C60,0x1D60,0x1E60,0x1F60,0x2040,0x2140, + 0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x2A60,0x2B60,0x2C60,0x2D60,0x2E60,0x2F60,0x3040,0x3140, + 0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x3A60,0x3B60,0x3C60,0x3D60,0x3E60,0x3F60,0x4040,0x4140, + 0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x4A60,0x4B60,0x4C60,0x4D60,0x4E60,0x4F60,0x5040,0x5140, + 0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x5A60,0x5B60,0x5C60,0x5D60,0x5E60,0x5F60,0x6040,0x6140, + 0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x6A60,0x6B60,0x6C60,0x6D60,0x6E60,0x6F60,0x7040,0x7140, + 0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x7A60,0x7B60,0x7C60,0x7D60,0x7E60,0x7F60,0x8040,0x8140, + 0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x8A60,0x8B60,0x8C60,0x8D60,0x8E60,0x8F60,0x9040,0x9140, + 0x9240,0x9340,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0x9A70,0x9B70,0x9C70,0x9D70,0x9E70,0x9F70,0xA050,0xA150, + 0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xAA70,0xAB70,0xAC70,0xAD70,0xAE70,0xAF70,0xB050,0xB150, + 0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xBA70,0xBB70,0xBC70,0xBD70,0xBE70,0xBF70,0xC050,0xC150, + 0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xCA70,0xCB70,0xCC70,0xCD70,0xCE70,0xCF70,0xD050,0xD150, + 0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xDA70,0xDB70,0xDC70,0xDD70,0xDE70,0xDF70,0xE050,0xE150, + 0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xEA70,0xEB70,0xEC70,0xED70,0xEE70,0xEF70,0xF050,0xF150, + 0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0xFA70,0xFB70,0xFC70,0xFD70,0xFE70,0xFF70,0x00D0,0x0150, + 0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x0A70,0x0B70,0x0C70,0x0D70,0x0E70,0x0F70,0x1050,0x1150, + 0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x1A70,0x1B70,0x1C70,0x1D70,0x1E70,0x1F70,0x2050,0x2150, + 0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x2A70,0x2B70,0x2C70,0x2D70,0x2E70,0x2F70,0x3050,0x3150, + 0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + }; + +u8 ZeroTable[] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 + }; + +#define GBSAVE_GAME_VERSION_1 1 +#define GBSAVE_GAME_VERSION_2 2 +#define GBSAVE_GAME_VERSION_3 3 +#define GBSAVE_GAME_VERSION_4 4 +#define GBSAVE_GAME_VERSION_5 5 +#define GBSAVE_GAME_VERSION_6 6 +#define GBSAVE_GAME_VERSION_7 7 +#define GBSAVE_GAME_VERSION_8 8 +#define GBSAVE_GAME_VERSION_9 9 +#define GBSAVE_GAME_VERSION_10 10 +#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_10 + +int inline gbGetValue(int min,int max,int v) +{ + return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0))); +} + +void gbGenFilter() +{ + for (int r=0;r<32;r++) + { + for (int g=0;g<32;g++) + { + for (int b=0;b<32;b++) + { + int nr=gbGetValue(gbGetValue(4,14,g), + gbGetValue(24,29,g),r)-4; + int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),b), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),b),g)-4; + int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),g), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),g),b)-4; + gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr; + } + } + } +} + +bool gbIsGameboyRom(char * file) +{ + if(strlen(file) > 4) + { + char * p = strrchr(file,'.'); + + if(p != NULL) + { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".gbc") == 0) + return true; + if(_stricmp(p, ".cgb") == 0) + return true; + if(_stricmp(p, ".sgb") == 0) + return true; + } + } + + return false; +} + +void gbCopyMemory(u16 d, u16 s, int count) +{ + while(count) + { + gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff]; + s++; + d++; + count--; + } +} + +void gbDoHdma() +{ + gbCopyMemory(gbHdmaDestination, + gbHdmaSource, + 0x10); + + gbHdmaDestination += 0x10; + gbHdmaSource += 0x10; + + register_HDMA2 += 0x10; + if(register_HDMA2 == 0x00) + register_HDMA1++; + + register_HDMA4 += 0x10; + if(register_HDMA4 == 0x00) + register_HDMA3++; + + if(gbHdmaDestination == 0x96b0) + gbHdmaBytes = gbHdmaBytes; + gbHdmaBytes -= 0x10; + register_HDMA5--; + if(register_HDMA5 == 0xff) + gbHdmaOn = 0; +} + +// fix for Harley and Lego Racers +void gbCompareLYToLYC() +{ + if(register_LY == register_LYC) + { + // mark that we have a match + register_STAT |= 4; + + // check if we need an interrupt + if((register_STAT & 0x40) && (register_IE & 2)) + gbInterrupt |= 2; + } + else // no match + register_STAT &= 0xfb; +} + +void gbWriteMemory(register u16 address, register u8 value) +{ + if(address < 0x8000) + { +#ifndef FINAL_VERSION + if(memorydebug && (address>0x3fff || address < 0x2000)) + { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } +#endif + if(mapper) + (*mapper)(address, value); + return; + } + + if(address < 0xa000) + { + gbMemoryMap[address>>12][address&0x0fff] = value; + return; + } + + if(address < 0xc000) + { +#ifndef FINAL_VERSION + if(memorydebug) + { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } +#endif + + if(mapper) + (*mapperRAM)(address, value); + return; + } + + if(address < 0xfe00) + { + gbMemoryMap[address>>12][address & 0x0fff] = value; + return; + } + + if(address < 0xff00) + { + gbMemory[address] = value; + return; + } + + switch(address & 0x00ff) + { + case 0x00: + { + gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | + (value & 0x30)); + if(gbSgbMode) + { + gbSgbDoBitTransfer(value); + } + + return; + } + + case 0x01: + { + gbMemory[0xff01] = value; + return; + } + + // serial control + case 0x02: + { + gbSerialOn = (value & 0x80); + gbMemory[0xff02] = value; + if(gbSerialOn) + { + gbSerialTicks = GBSERIAL_CLOCK_TICKS; +#ifdef LINK_EMULATION + if(linkConnected) + { + if(value & 1) + { + linkSendByte(0x100|gbMemory[0xFF01]); + Sleep(5); + } + } +#endif + } + + gbSerialBits = 0; + return; + } + + // DIV register resets on any write + case 0x04: + { + register_DIV = 0; + return; + } + case 0x05: + register_TIMA = value; + return; + + case 0x06: + register_TMA = value; + return; + + // TIMER control + case 0x07: + { + register_TAC = value; + + gbTimerOn = (value & 4); + gbTimerMode = value & 3; + // register_TIMA = register_TMA; + switch(gbTimerMode) + { + case 0: + gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; + break; + case 1: + gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_1_CLOCK_TICKS; + break; + case 2: + gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_2_CLOCK_TICKS; + break; + case 3: + gbTimerClockTicks = gbTimerTicks = GBTIMER_MODE_3_CLOCK_TICKS; + break; + } + return; + } + + case 0x0f: + { + register_IF = value; + gbInterrupt = value; + return; + } + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + { + SOUND_EVENT(address,value); + return; + } + case 0x40: + { + int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); + + if(lcdChange) + { + if(value & 0x80) + { + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS; + gbLcdMode = 0; + register_STAT &= 0xfc; + register_LY = 0x00; + } + else + { + gbLcdTicks = 0; + gbLcdMode = 0; + register_STAT &= 0xfc; + register_LY = 0x00; + } + // compareLYToLYC(); + } + // don't draw the window if it was not enabled and not being drawn before + if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && + register_LY > register_WY) + gbWindowLine = 144; + + register_LCDC = value; + + return; + } + + // STAT + case 0x41: + { + //register_STAT = (register_STAT & 0x87) | + // (value & 0x7c); + register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? + // GB bug from Devrs FAQ + if(!gbCgbMode && (register_LCDC & 0x80) && gbLcdMode < 2) + gbInterrupt |= 2; + return; + } + + // SCY + case 0x42: + { + register_SCY = value; + return; + } + + // SCX + case 0x43: + { + register_SCX = value; + return; + } + + // LY + case 0x44: + { + // read only + return; + } + + // LYC + case 0x45: + { + register_LYC = value; + if((register_LCDC & 0x80)) + { + gbCompareLYToLYC(); + } + return; + } + + // DMA! + case 0x46: + { + int source = value * 0x0100; + + gbCopyMemory(0xfe00, + source, + 0xa0); + register_DMA = value; + return; + } + + // BGP + case 0x47: + { + gbBgp[0] = value & 0x03; + gbBgp[1] = (value & 0x0c)>>2; + gbBgp[2] = (value & 0x30)>>4; + gbBgp[3] = (value & 0xc0)>>6; + break; + } + + // OBP0 + case 0x48: + { + gbObp0[0] = value & 0x03; + gbObp0[1] = (value & 0x0c)>>2; + gbObp0[2] = (value & 0x30)>>4; + gbObp0[3] = (value & 0xc0)>>6; + break; + } + + // OBP1 + case 0x49: + { + gbObp1[0] = value & 0x03; + gbObp1[1] = (value & 0x0c)>>2; + gbObp1[2] = (value & 0x30)>>4; + gbObp1[3] = (value & 0xc0)>>6; + break; + } + + case 0x4a: + register_WY = value; + return; + + case 0x4b: + register_WX = value; + return; + + // KEY1 + case 0x4d: + { + if(gbCgbMode) + { + gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1); + return; + } + } + break; + + // VBK + case 0x4f: + { + if(gbCgbMode) + { + value = value & 1; + if(value == gbVramBank) + return; + + int vramAddress = value * 0x2000; + gbMemoryMap[0x08] = &gbVram[vramAddress]; + gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; + + gbVramBank = value; + register_VBK = value; + } + return; + } + break; + + // HDMA1 + case 0x51: + { + if(gbCgbMode) + { + if(value > 0x7f && value < 0xa0) + value = 0; + + gbHdmaSource = (value << 8) | (register_HDMA2 & 0xf0); + + register_HDMA1 = value; + return; + } + } + break; + + // HDMA2 + case 0x52: + { + if(gbCgbMode) + { + value = value & 0xf0; + + gbHdmaSource = (register_HDMA1 << 8) | (value); + + register_HDMA2 = value; + return; + } + } + break; + + // HDMA3 + case 0x53: + { + if(gbCgbMode) + { + value = value & 0x1f; + gbHdmaDestination = (value << 8) | (register_HDMA4 & 0xf0); + gbHdmaDestination += 0x8000; + register_HDMA3 = value; + return; + } + } + break; + + // HDMA4 + case 0x54: + { + if(gbCgbMode) + { + value = value & 0xf0; + gbHdmaDestination = ((register_HDMA3 & 0x1f) << 8) | value; + gbHdmaDestination += 0x8000; + register_HDMA4 = value; + return; + } + } + break; + + // HDMA5 + case 0x55: + { + if(gbCgbMode) + { + gbHdmaBytes = 16 + (value & 0x7f) * 16; + if(gbHdmaOn) + { + if(value & 0x80) + { + register_HDMA5 = (value & 0x7f); + } + else + { + register_HDMA5 = 0xff; + gbHdmaOn = 0; + } + } + else + { + if(value & 0x80) + { + gbHdmaOn = 1; + register_HDMA5 = value & 0x7f; + if(gbLcdMode == 0) + gbDoHdma(); + } + else + { + // we need to take the time it takes to complete the transfer into + // account... according to GB DEV FAQs, the setup time is the same + // for single and double speed, but the actual transfer takes the + // same time + if(gbSpeed) + gbDmaTicks = 231 + 16 * (value & 0x7f); + else + gbDmaTicks = 231 + 8 * (value & 0x7f); + gbCopyMemory(gbHdmaDestination, + gbHdmaSource, + gbHdmaBytes); + gbHdmaDestination += gbHdmaBytes; + gbHdmaSource += gbHdmaBytes; + + register_HDMA3 = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; + register_HDMA4 = gbHdmaDestination & 0xf0; + register_HDMA1 = (gbHdmaSource >> 8) & 0xff; + register_HDMA2 = gbHdmaSource & 0xf0; + } + } + return; + } + } + break; + + // BCPS + case 0x68: + { + if(gbCgbMode) + { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + gbMemory[0xff68] = value; + gbMemory[0xff69] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // BCPD + case 0x69: + { + if(gbCgbMode) + { + int v = gbMemory[0xff68]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + gbMemory[0xff69] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + + if(gbMemory[0xff68] & 0x80) + { + int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; + + gbMemory[0xff69] = (index & 1 ? + (gbPalette[index>>1] >> 8) : + (gbPalette[index>>1] & 0x00ff)); + + } + return; + } + } + break; + + // OCPS + case 0x6a: + { + if(gbCgbMode) + { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + paletteIndex += 32; + + gbMemory[0xff6a] = value; + gbMemory[0xff6b] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // OCPD + case 0x6b: + { + if(gbCgbMode) + { + int v = gbMemory[0xff6a]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + paletteIndex += 32; + + gbMemory[0xff6b] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + if(gbMemory[0xff6a] & 0x80) + { + int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; + + gbMemory[0xff6b] = (index & 1 ? + (gbPalette[(index>>1) + 32] >> 8) : + (gbPalette[(index>>1) + 32] & 0x00ff)); + + } + return; + } + } + break; + + // SVBK + case 0x70: + { + if(gbCgbMode) + { + value = value & 7; + + int bank = value; + if(value == 0) + bank = 1; + + if(bank == gbWramBank) + return; + + int wramAddress = bank * 0x1000; + gbMemoryMap[0x0d] = &gbWram[wramAddress]; + + gbWramBank = bank; + register_SVBK = value; + return; + } + } + break; + + case 0xff: + { + register_IE = value; + register_IF &= value; + return; + } + } + + gbMemory[address] = value; +} + +u8 gbReadOpcode(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + switch(address & 0xf000) + { + case 0x0a: + case 0x0b: + if(mapperReadRAM) + return mapperReadRAM(address); + break; + case 0x0f: + if(address > 0xff00) + { + switch(address & 0x00ff) + { + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x0f: + return (0xe0 | register_IF); + case 0x40: + return register_LCDC; + case 0x41: + return (0x80 | register_STAT); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x70: + return (0xf8 | register_SVBK); + case 0xff: + return register_IE; + } + } + break; + } + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +u8 gbReadMemory(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + if(address < 0xa000) + return gbMemoryMap[address>>12][address&0x0fff]; + + if(address < 0xc000) + { +#ifndef FINAL_VERSION + if(memorydebug) + { + log("Memory register read %04x PC=%04x\n", + address, + PC.W); + } +#endif + + if(mapperReadRAM) + return mapperReadRAM(address); + return gbMemoryMap[address>>12][address & 0x0fff]; + } + + if(address >= 0xff00) + { + switch(address & 0x00ff) + { + case 0x00: + { + if(gbSgbMode) + { + gbSgbReadingController |= 4; + gbSgbResetPacketState(); + } + + int b = gbMemory[0xff00]; + + if((b & 0x30) == 0x20) + { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) + { + switch(gbSgbNextController) + { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 128)) + b |= 0x08; + if(!(joystate & 64)) + b |= 0x04; + if(!(joystate & 32)) + b |= 0x02; + if(!(joystate & 16)) + b |= 0x01; + + gbMemory[0xff00] = b; + } + else if((b & 0x30) == 0x10) + { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) + { + switch(gbSgbNextController) + { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 8)) + b |= 0x08; + if(!(joystate & 4)) + b |= 0x04; + if(!(joystate & 2)) + b |= 0x02; + if(!(joystate & 1)) + b |= 0x01; + + gbMemory[0xff00] = b; + } + else + { + if(gbSgbMode && gbSgbMultiplayer) + { + gbMemory[0xff00] = 0xf0 | gbSgbNextController; + } + else + { + gbMemory[0xff00] = 0xff; + } + } + } + return gbMemory[0xff00]; + break; + case 0x01: + return gbMemory[0xff01]; + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x0f: + return (0xe0 | register_IF); + case 0x40: + return register_LCDC; + case 0x41: + return (0x80 | register_STAT); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x70: + return (0xf8 | register_SVBK); + case 0xff: + return register_IE; + } + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +void gbVblank_interrupt() +{ + if(IFF & 0x80) + { + PC.W++; + IFF &= 0x7f; + } + gbInterrupt &= 0xfe; + + IFF &= 0x7e; + register_IF &= 0xfe; + + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x40; +} + +void gbLcd_interrupt() +{ + if(IFF & 0x80) + { + PC.W++; + IFF &= 0x7f; + } + gbInterrupt &= 0xfd; + IFF &= 0x7e; + register_IF &= 0xfd; + + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + + PC.W = 0x48; +} + +void gbTimer_interrupt() +{ + if(IFF & 0x80) + { + PC.W++; + IFF &= 0x7f; + } + IFF &= 0x7e; + gbInterrupt &= 0xfb; + register_IF &= 0xfb; + + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + + PC.W = 0x50; +} + +void gbSerial_interrupt() +{ + if(IFF & 0x80) + { + PC.W++; + IFF &= 0x7f; + } + IFF &= 0x7e; + gbInterrupt &= 0xf7; + register_IF &= 0xf7; + + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + + PC.W = 0x58; +} + +void gbJoypad_interrupt() +{ + if(IFF & 0x80) + { + PC.W++; + IFF &= 0x7f; + } + IFF &= 0x7e; + gbInterrupt &= 0xef; + register_IF &= 0xef; + + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + + PC.W = 0x60; +} + +void gbSpeedSwitch() +{ + if(gbSpeed == 0) + { + gbSpeed = 1; + GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; //127; //51 * 2; + GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; + GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; //52; //20 * 2; + GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; //99; //43 * 2; + GBDIV_CLOCK_TICKS = 64 * 2; + GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; + GBTIMER_MODE_0_CLOCK_TICKS = 256; //256*2; + GBTIMER_MODE_1_CLOCK_TICKS = 4; //4*2; + GBTIMER_MODE_2_CLOCK_TICKS = 16; //16*2; + GBTIMER_MODE_3_CLOCK_TICKS = 64; //64*2; + GBSERIAL_CLOCK_TICKS = 128 * 2; + gbDivTicks *= 2; + gbLcdTicks *= 2; + gbLcdLYIncrementTicks *= 2; + // timerTicks *= 2; + // timerClockTicks *= 2; + gbSerialTicks *= 2; + SOUND_CLOCK_TICKS = soundQuality * 24 * 2; + soundTicks *= 2; + // synchronizeTicks *= 2; + // SYNCHRONIZE_CLOCK_TICKS *= 2; + } + else + { + gbSpeed = 0; + GBLCD_MODE_0_CLOCK_TICKS = 51; + GBLCD_MODE_1_CLOCK_TICKS = 1140; + GBLCD_MODE_2_CLOCK_TICKS = 20; + GBLCD_MODE_3_CLOCK_TICKS = 43; + GBDIV_CLOCK_TICKS = 64; + GBLY_INCREMENT_CLOCK_TICKS = 114; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128; + gbDivTicks /= 2; + gbLcdTicks /= 2; + gbLcdLYIncrementTicks /= 2; + // timerTicks /= 2; + // timerClockTicks /= 2; + gbSerialTicks /= 2; + SOUND_CLOCK_TICKS = soundQuality * 24; + soundTicks /= 2; + // synchronizeTicks /= 2; + // SYNCHRONIZE_CLOCK_TICKS /= 2; + } +} + +void gbReset() +{ + SP.W = 0xfffe; + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + PC.W = 0x0100; + IFF = 0; + gbInterrupt = 1; + gbInterruptWait = 0; + + register_DIV = 0; + register_TIMA = 0; + register_TMA = 0; + register_TAC = 0; + register_IF = 1; + register_LCDC = 0x91; + register_STAT = 0; + register_SCY = 0; + register_SCX = 0; + register_LY = 0; + register_LYC = 0; + register_DMA = 0; + register_WY = 0; + register_WX = 0; + register_VBK = 0; + register_HDMA1 = 0; + register_HDMA2 = 0; + register_HDMA3 = 0; + register_HDMA4 = 0; + register_HDMA5 = 0; + register_SVBK = 0; + register_IE = 0; + + if(gbCgbMode) + { + if(gbSgbMode) + { + if(gbEmulatorType == 5) + AF.W = 0xffb0; + else + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + else + { + AF.W = 0x11b0; + BC.W = 0x0000; + DE.W = 0xff56; + HL.W = 0x000d; + } + if(gbEmulatorType == 4) + BC.B.B1 |= 0x01; + + register_HDMA5 = 0xff; + gbMemory[0xff68] = 0xc0; + gbMemory[0xff6a] = 0xc0; + } + else + { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + + if(gbSpeed) + { + gbSpeedSwitch(); + gbMemory[0xff4d] = 0; + } + + gbDivTicks = GBDIV_CLOCK_TICKS; + gbLcdMode = 2; + gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS; + gbLcdLYIncrementTicks = 0; + gbTimerTicks = 0; + gbTimerClockTicks = 0; + gbSerialTicks = 0; + gbSerialBits = 0; + gbSerialOn = 0; + gbWindowLine = -1; + gbTimerOn = 0; + gbTimerMode = 0; + // gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; + gbSpeed = 0; + gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; + + if(gbCgbMode) + { + gbSpeed = 0; + gbHdmaOn = 0; + gbHdmaSource = 0x0000; + gbHdmaDestination = 0x8000; + gbVramBank = 0; + gbWramBank = 1; + register_LY = 0x90; + gbLcdMode = 1; + for(int i = 0; i < 64; i++) + gbPalette[i] = 0x7fff; + } + + if(gbSgbMode) + { + gbSgbReset(); + } + + for(int i =0; i < 4; i++) + gbBgp[i] = gbObp0[i] = gbObp1[i] = i; + + memset(&gbDataMBC1,0, sizeof(gbDataMBC1)); + gbDataMBC1.mapperROMBank = 1; + + gbDataMBC2.mapperRAMEnable = 0; + gbDataMBC2.mapperROMBank = 1; + + memset(&gbDataMBC3,0, 6 * sizeof(int)); + gbDataMBC3.mapperROMBank = 1; + + memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); + gbDataMBC5.mapperROMBank = 1; + + memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); + gbDataHuC1.mapperROMBank = 1; + + memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); + gbDataHuC3.mapperROMBank = 1; + + gbMemoryMap[0x00] = &gbRom[0x0000]; + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + if(gbCgbMode) + { + gbMemoryMap[0x08] = &gbVram[0x0000]; + gbMemoryMap[0x09] = &gbVram[0x1000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbWram[0x1000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } + else + { + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } + + if(gbRam) + { + gbMemoryMap[0x0a] = &gbRam[0x0000]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbSoundReset(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbLastTime = systemGetClock(); + gbFrameCount = 0; +} + +void gbWriteSaveMBC1(const char * name) +{ + FILE* gzFile = gen_fopen(name,"wb"); + + if(gzFile == NULL) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + gen_fwrite(gbRam, + 1, + gbRamSize, + gzFile); + + gen_fclose(gzFile); +} + +void gbWriteSaveMBC2(const char * name) +{ + FILE* file = gen_fopen(name, "wb"); + + if(file == NULL) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + gen_fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + gen_fclose(file); +} + +void gbWriteSaveMBC3(const char * name, bool extendedSave) +{ + FILE* gzFile = gen_fopen(name,"wb"); + + if(gzFile == NULL) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + gen_fwrite(gbRam, + 1, + gbRamSize, + gzFile); + + if(extendedSave) + gen_fwrite(&gbDataMBC3.mapperSeconds, + 1, + 10*sizeof(int) + sizeof(time_t), + gzFile); + + gen_fclose(gzFile); +} + +void gbWriteSaveMBC5(const char * name) +{ + FILE* gzFile = gen_fopen(name,"wb"); + + if(gzFile == NULL) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + gen_fwrite(gbRam, + 1, + gbRamSize, + gzFile); + + gen_fclose(gzFile); +} + +void gbWriteSaveMBC7(const char * name) +{ + FILE* file = gen_fopen(name, "wb"); + + if(file == NULL) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + gen_fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + gen_fclose(file); +} + +bool gbReadSaveMBC1(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) + { + return false; + } + + int read = gzread(gzFile, + gbRam, + gbRamSize); + + if(read != gbRamSize) + { + systemMessage(MSG_FAILED_TO_READ_SGM, N_("Failed to read complete save game %s (%d)"), name, read); + gzclose(gzFile); + return false; + } + + gzclose(gzFile); + return true; +} + +bool gbReadSaveMBC2(const char * name) +{ + FILE* file = gen_fopen(name, "rb"); + + if(file == NULL) + { + return false; + } + + int read = gen_fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) + { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Failed to read complete save game %s (%d)"), name, read); + gen_fclose(file); + return false; + } + + gen_fclose(file); + return true; +} + +bool gbReadSaveMBC3(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) + { + return false; + } + + int read = gzread(gzFile, + gbRam, + gbRamSize); + + bool res = true; + + if(read != gbRamSize) + { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Failed to read complete save game %s (%d)"), name, read); + } + else + { + read = gzread(gzFile, + &gbDataMBC3.mapperSeconds, + sizeof(int)*10 + sizeof(time_t)); + + if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC, + N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + } + + gzclose(gzFile); + return res; +} + +bool gbReadSaveMBC5(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) + { + return false; + } + + int read = gzread(gzFile, + gbRam, + gbRamSize); + + if(read != gbRamSize) + { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Failed to read complete save game %s (%d)"), name, read); + gzclose(gzFile); + return false; + } + + gzclose(gzFile); + return true; +} + +bool gbReadSaveMBC7(const char * name) +{ + FILE* file = gen_fopen(name, "rb"); + + if(file == NULL) + { + return false; + } + + int read = gen_fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) + { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Failed to read complete save game %s (%d)"), name, read); + gen_fclose(file); + return false; + } + + gen_fclose(file); + return true; +} + +void gbInit() +{ + gbGenFilter(); + gbSgbInit(); + + gbMemory = (u8 *)malloc(65536); + memset(gbMemory,0, 65536); + + pix = (u8 *)calloc(1,4*257*226); + + gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); +} + +bool gbWriteBatteryFile(const char *file, bool extendedSave) +{ + if(gbBattery) + { + int type = gbRom[0x147]; + + switch(type) + { + case 0x03: + gbWriteSaveMBC1(file); + break; + case 0x06: + gbWriteSaveMBC2(file); + break; + case 0x0f: + case 0x10: + case 0x13: + gbWriteSaveMBC3(file, extendedSave); + break; + case 0x1b: + case 0x1e: + gbWriteSaveMBC5(file); + break; + case 0x22: + gbWriteSaveMBC7(file); + break; + case 0xff: + gbWriteSaveMBC1(file); + break; + } + } + return true; +} + +bool gbWriteBatteryFile(const char *file) +{ + gbWriteBatteryFile(file, true); + return true; +} + +bool gbReadBatteryFile(const char *file) +{ + bool res = false; + if(gbBattery) + { + int type = gbRom[0x147]; + + switch(type) + { + case 0x03: + res = gbReadSaveMBC1(file); + break; + case 0x06: + res = gbReadSaveMBC2(file); + break; + case 0x0f: + case 0x10: + case 0x13: + if(!gbReadSaveMBC3(file)) + { + time(&gbDataMBC3.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataMBC3.mapperLastTime); + gbDataMBC3.mapperSeconds = lt->tm_sec; + gbDataMBC3.mapperMinutes = lt->tm_min; + gbDataMBC3.mapperHours = lt->tm_hour; + gbDataMBC3.mapperDays = lt->tm_yday & 255; + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0x1b: + case 0x1e: + res = gbReadSaveMBC5(file); + break; + case 0x22: + res = gbReadSaveMBC7(file); + case 0xff: + res = gbReadSaveMBC1(file); + break; + } + } + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + return res; +} + +bool gbReadGSASnapshot(const char *fileName) +{ + FILE* file = gen_fopen(fileName, "rb"); + + if(!file) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // long size = ftell(file); + gen_fseek(file, 0x4, SEEK_SET); + char buffer[16]; + char buffer2[16]; + gen_fread(buffer, 1, 15, file); + buffer[15] = 0; + memcpy(buffer2, &gbRom[0x134], 15); + buffer2[15] = 0; + if(memcmp(buffer, buffer2, 15)) + { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + gen_fclose(file); + return false; + } + gen_fseek(file, 0x13, SEEK_SET); + int read = 0; + int toRead = 0; + switch(gbRom[0x147]) + { + case 0x03: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1e: + case 0xff: + read = gen_fread(gbRam, 1, gbRamSize, file); + toRead = gbRamSize; + break; + case 0x06: + case 0x22: + read = gen_fread(&gbMemory[0xa000],1,256,file); + toRead = 256; + break; + default: + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + gen_fclose(file); + return false; + } + gen_fclose(file); + gbReset(); + return true; +} + +variable_desc gbSaveGameStruct[] = { + { &PC.W, sizeof(u16) }, + { &SP.W, sizeof(u16) }, + { &AF.W, sizeof(u16) }, + { &BC.W, sizeof(u16) }, + { &DE.W, sizeof(u16) }, + { &HL.W, sizeof(u16) }, + { &IFF, sizeof(u8) }, + { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBDIV_CLOCK_TICKS, sizeof(int) }, + { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBSERIAL_CLOCK_TICKS, sizeof(int) }, + { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) }, + { &gbDivTicks, sizeof(int) }, + { &gbLcdMode, sizeof(int) }, + { &gbLcdTicks, sizeof(int) }, + { &gbLcdLYIncrementTicks, sizeof(int) }, + { &gbTimerTicks, sizeof(int) }, + { &gbTimerClockTicks, sizeof(int) }, + { &gbSerialTicks, sizeof(int) }, + { &gbSerialBits, sizeof(int) }, + { &gbInterrupt, sizeof(int) }, + { &gbInterruptWait, sizeof(int) }, + { &gbSynchronizeTicks, sizeof(int) }, + { &gbTimerOn, sizeof(int) }, + { &gbTimerMode, sizeof(int) }, + { &gbSerialOn, sizeof(int) }, + { &gbWindowLine, sizeof(int) }, + { &gbCgbMode, sizeof(int) }, + { &gbVramBank, sizeof(int) }, + { &gbWramBank, sizeof(int) }, + { &gbHdmaSource, sizeof(int) }, + { &gbHdmaDestination, sizeof(int) }, + { &gbHdmaBytes, sizeof(int) }, + { &gbHdmaOn, sizeof(int) }, + { &gbSpeed, sizeof(int) }, + { &gbSgbMode, sizeof(int) }, + { ®ister_DIV, sizeof(u8) }, + { ®ister_TIMA, sizeof(u8) }, + { ®ister_TMA, sizeof(u8) }, + { ®ister_TAC, sizeof(u8) }, + { ®ister_IF, sizeof(u8) }, + { ®ister_LCDC, sizeof(u8) }, + { ®ister_STAT, sizeof(u8) }, + { ®ister_SCY, sizeof(u8) }, + { ®ister_SCX, sizeof(u8) }, + { ®ister_LY, sizeof(u8) }, + { ®ister_LYC, sizeof(u8) }, + { ®ister_DMA, sizeof(u8) }, + { ®ister_WY, sizeof(u8) }, + { ®ister_WX, sizeof(u8) }, + { ®ister_VBK, sizeof(u8) }, + { ®ister_HDMA1, sizeof(u8) }, + { ®ister_HDMA2, sizeof(u8) }, + { ®ister_HDMA3, sizeof(u8) }, + { ®ister_HDMA4, sizeof(u8) }, + { ®ister_HDMA5, sizeof(u8) }, + { ®ister_SVBK, sizeof(u8) }, + { ®ister_IE , sizeof(u8) }, + { &gbBgp[0], sizeof(u8) }, + { &gbBgp[1], sizeof(u8) }, + { &gbBgp[2], sizeof(u8) }, + { &gbBgp[3], sizeof(u8) }, + { &gbObp0[0], sizeof(u8) }, + { &gbObp0[1], sizeof(u8) }, + { &gbObp0[2], sizeof(u8) }, + { &gbObp0[3], sizeof(u8) }, + { &gbObp1[0], sizeof(u8) }, + { &gbObp1[1], sizeof(u8) }, + { &gbObp1[2], sizeof(u8) }, + { &gbObp1[3], sizeof(u8) }, + { NULL, 0 } + }; + + +static bool gbWriteSaveState(gzFile gzFile) +{ + utilWriteInt(gzFile, GBSAVE_GAME_VERSION); + + utilGzWrite(gzFile, &gbRom[0x134], 15); + + utilWriteData(gzFile, gbSaveGameStruct); + + utilGzWrite(gzFile, &IFF, 2); + + if(gbSgbMode) + { + gbSgbSaveGame(gzFile); + } + + utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + + // not saved anymore + // gzwrite(gzFile, pix, 256*224*sizeof(u16)); + + utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); + // todo: remove + utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); + + utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) + { + utilGzWrite(gzFile, gbRam, gbRamSize); + } + + if(gbCgbMode) + { + utilGzWrite(gzFile, gbVram, 0x4000); + utilGzWrite(gzFile, gbWram, 0x8000); + } + + gbSoundSaveGame(gzFile); + + gbCheatsSaveGame(gzFile); + + return true; +} + +bool gbWriteMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) + { + return false; + } + + bool res = gbWriteSaveState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +bool gbWriteSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"wb"); + + if(gzFile == NULL) + return false; + + bool res = gbWriteSaveState(gzFile); + + utilGzClose(gzFile); + return res; +} + +static bool gbReadSaveState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > GBSAVE_GAME_VERSION || version < 0) + { + systemMessage(MSG_UNSUPPORTED_VB_SGM, + N_("Unsupported VisualBoy save game version %d"), version); + return false; + } + + u8 romname[20]; + + utilGzRead(gzFile, romname, 15); + + if(memcmp(&gbRom[0x134], romname, 15) != 0) + { + systemMessage(MSG_CANNOT_LOAD_SGM_FOR, + N_("Cannot load save game for %s. Playing %s"), + romname, &gbRom[0x134]); + return false; + } + + utilReadData(gzFile, gbSaveGameStruct); + + if(version >= GBSAVE_GAME_VERSION_7) + { + utilGzRead(gzFile, &IFF, 2); + } + + if(gbSgbMode) + { + gbSgbReadGame(gzFile, version); + } + else + { + gbSgbMask = 0; // loading a game at the wrong time causes no display + } + + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + if(version < GBSAVE_GAME_VERSION_4) + // prior to version 4, there was no adjustment for the time the game + // was last played, so we have less to read. This needs update if the + // structure changes again. + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t)); + else + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + + if(version < GBSAVE_GAME_VERSION_5) + { + utilGzRead(gzFile, pix, 256*224*sizeof(u16)); + } + memset(pix, 0, 257*226*sizeof(u32)); + + if(version < GBSAVE_GAME_VERSION_6) + { + utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); + } + else + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + // todo: remove + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if(version < GBSAVE_GAME_VERSION_10) + { + if(!gbCgbMode && !gbSgbMode) + { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + } + + utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) + { + utilGzRead(gzFile, gbRam, gbRamSize); + } + + gbMemoryMap[0x00] = &gbRom[0x0000]; + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + + int type = gbRom[0x147]; + + switch(type) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + // MBC 1 + memoryUpdateMapMBC1(); + break; + case 0x05: + case 0x06: + // MBC2 + memoryUpdateMapMBC2(); + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // MBC 3 + memoryUpdateMapMBC3(); + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + memoryUpdateMapMBC5(); + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + memoryUpdateMapMBC5(); + break; + case 0x22: + // MBC 7 + memoryUpdateMapMBC7(); + break; + case 0xfe: + // HuC3 + memoryUpdateMapHuC3(); + break; + case 0xff: + // HuC1 + memoryUpdateMapHuC1(); + break; + } + + if(gbCgbMode) + { + utilGzRead(gzFile, gbVram, 0x4000); + utilGzRead(gzFile, gbWram, 0x8000); + + int value = register_SVBK; + if(value == 0) + value = 1; + + gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; + gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; + gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; + } + + gbSoundReadGame(version, gzFile); + + if(gbBorderOn) + { + gbSgbRenderBorder(); + } + + systemDrawScreen(); + + if(version > GBSAVE_GAME_VERSION_1) + gbCheatsReadGame(gzFile, version); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + return true; +} + +bool gbReadMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbReadSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"rb"); + + if(gzFile == NULL) + { + return false; + } + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbWritePNGFile(const char *fileName) +{ + if(gbBorderOn) + return utilWritePNGFile(fileName, 256, 224, pix); + return utilWritePNGFile(fileName, 160, 144, pix); +} + +bool gbWriteBMPFile(const char *fileName) +{ + if(gbBorderOn) + return utilWriteBMPFile(fileName, 256, 224, pix); + return utilWriteBMPFile(fileName, 160, 144, pix); +} + +void gbCleanUp() +{ + if(gbRam != NULL) + { + free(gbRam); + gbRam = NULL; + } + + if(gbRom != NULL) + { + free(gbRom); + gbRom = NULL; + } + + if(gbMemory != NULL) + { + free(gbMemory); + gbMemory = NULL; + } + + if(gbLineBuffer != NULL) + { + free(gbLineBuffer); + gbLineBuffer = NULL; + } + + if(pix != NULL) + { + free(pix); + pix = NULL; + } + + gbSgbShutdown(); + + if(gbVram != NULL) + { + free(gbVram); + gbVram = NULL; + } + + if(gbWram != NULL) + { + free(gbWram); + gbWram = NULL; + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +} + +bool gbLoadRom(const char *szFile) +{ + int size = 0; + + if(gbRom != NULL) + { + gbCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbRom = utilLoad(szFile, + utilIsGBImage, + NULL, + size); + if(!gbRom) + return false; + + gbRomSize = size; + + return gbUpdateSizes(); +} + +bool gbUpdateSizes() +{ + if(gbRom[0x148] > 8) + { + systemMessage(MSG_UNSUPPORTED_ROM_SIZE, + N_("Unsupported rom size %02x"), gbRom[0x148]); + return false; + } + + if(gbRomSize < gbRomSizes[gbRom[0x148]]) + { + gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + } + gbRomSize = gbRomSizes[gbRom[0x148]]; + gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; + + if(gbRom[0x149] > 5) + { + systemMessage(MSG_UNSUPPORTED_RAM_SIZE, + N_("Unsupported ram size %02x"), gbRom[0x149]); + return false; + } + + gbRamSize = gbRamSizes[gbRom[0x149]]; + gbRamSizeMask = gbRamSizesMasks[gbRom[0x149]]; + + if(gbRamSize) + { + gbRam = (u8 *)malloc(gbRamSize); + memset(gbRam, 0xFF, gbRamSize); + } + + int type = gbRom[0x147]; + + mapperReadRAM = NULL; + + switch(type) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + // MBC 1 + mapper = mapperMBC1ROM; + mapperRAM = mapperMBC1RAM; + break; + case 0x05: + case 0x06: + // MBC2 + mapper = mapperMBC2ROM; + mapperRAM = mapperMBC2RAM; + gbRamSize = 0x200; + gbRamSizeMask = 0x1ff; + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // MBC 3 + mapper = mapperMBC3ROM; + mapperRAM = mapperMBC3RAM; + mapperReadRAM = mapperMBC3ReadRAM; + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + break; + case 0x22: + // MBC 7 + mapper = mapperMBC7ROM; + mapperRAM = mapperMBC7RAM; + mapperReadRAM = mapperMBC7ReadRAM; + break; + case 0xfe: + // HuC3 + mapper = mapperHuC3ROM; + mapperRAM = mapperHuC3RAM; + mapperReadRAM = mapperHuC3ReadRAM; + break; + case 0xff: + // HuC1 + mapper = mapperHuC1ROM; + mapperRAM = mapperHuC1RAM; + break; + default: + systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, + N_("Unknown cartridge type %02x"), type); + return false; + } + + switch(type) + { + case 0x03: + case 0x06: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1d: + case 0x1e: + case 0x22: + case 0xff: + gbBattery = 1; + break; + } + + if(gbRom[0x146] == 0x03) + { + if(gbEmulatorType == 0 || + gbEmulatorType == 2 || + gbEmulatorType == 5) + gbSgbMode = 1; + else + gbSgbMode = 0; + } + else + gbSgbMode = 0; + + if(gbRom[0x143] & 0x80) + { + if(gbEmulatorType == 0 || + gbEmulatorType == 1 || + gbEmulatorType == 4 || + gbEmulatorType == 5) + { + gbCgbMode = 1; + gbVram = (u8 *)malloc(0x4000); + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbWram,0,0x8000); + memset(gbPalette,0, 2*128); + } + else + { + gbCgbMode = 0; + } + } + else + gbCgbMode = 0; + + gbInit(); + gbReset(); + + switch(type) + { + case 0x1c: + case 0x1d: + case 0x1e: + gbDataMBC5.isRumbleCartridge = 1; + } + + return true; +} + +void gbEmulate(int ticksToStop) +{ + gbRegister tempRegister; + u8 tempValue; + s8 offset; + + int clockTicks = 0; + gbDmaTicks = 0; + + register int opcode = 0; + + while(1) + { +#ifndef FINAL_VERSION + if(systemDebug) + { + if(!(IFF & 0x80)) + { + if(systemDebug > 1) + { + sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", + PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF); + } + else + { + sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF); + } + log(gbBuffer); + } + } +#endif + if(IFF & 0x80) + { + if(register_LCDC & 0x80) + { + clockTicks = gbLcdTicks; + } + else + clockTicks = 100; + + if(gbLcdMode == 1 && (gbLcdLYIncrementTicks < clockTicks)) + clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < clockTicks)) + clockTicks = gbSerialTicks; + + if(gbTimerOn && (gbTimerTicks < clockTicks)) + clockTicks = gbTimerTicks; + + if(soundTicks && (soundTicks < clockTicks)) + clockTicks = soundTicks; + } + else + { + opcode = gbReadOpcode(PC.W++); + + if(IFF & 0x100) + { + IFF &= 0xff; + PC.W--; + } + + clockTicks = gbCycles[opcode]; + + switch(opcode) + { + case 0xCB: + // extended opcode + opcode = gbReadOpcode(PC.W++); + clockTicks = gbCyclesCB[opcode]; + switch(opcode) + { +#include "gbCodesCB.h" + } + break; +#include "gbCodes.h" + } + } + + if(!emulating) + return; + + if(gbDmaTicks) + { + clockTicks += gbDmaTicks; + gbDmaTicks = 0; + } + + if(gbSgbMode) + { + if(gbSgbPacketTimeout) + { + gbSgbPacketTimeout -= clockTicks; + + if(gbSgbPacketTimeout <= 0) + gbSgbResetPacketState(); + } + } + + ticksToStop -= clockTicks; + + // DIV register emulation + gbDivTicks -= clockTicks; + while(gbDivTicks <= 0) + { + register_DIV++; + gbDivTicks += GBDIV_CLOCK_TICKS; + } + + if(register_LCDC & 0x80) + { + // LCD stuff + gbLcdTicks -= clockTicks; + if(gbLcdMode == 1) + { + // during V-BLANK,we need to increment LY at the same rate! + gbLcdLYIncrementTicks -= clockTicks; + while(gbLcdLYIncrementTicks <= 0) + { + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + + if(register_LY < 153) + { + register_LY++; + + gbCompareLYToLYC(); + + if(register_LY >= 153) + gbLcdLYIncrementTicks = 6; + } + else + { + register_LY = 0x00; + // reset the window line + gbWindowLine = -1; + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS * 2; + gbCompareLYToLYC(); + } + } + } + + // our counter is off, see what we need to do + while(gbLcdTicks <= 0) + { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + switch(gbLcdMode) + { + case 0: + // H-Blank + register_LY++; + + gbCompareLYToLYC(); + + // check if we reached the V-Blank period + if(register_LY == 144) + { + // Yes, V-Blank + // set the LY increment counter + gbLcdLYIncrementTicks = gbLcdTicks + GBLY_INCREMENT_CLOCK_TICKS; + gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdMode = 1; + if(register_LCDC & 0x80) + { + gbInterrupt |= 1; // V-Blank interrupt + gbInterruptWait = 6; + if(register_STAT & 0x10) + gbInterrupt |= 2; + } + + gbFrameCount++; + + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) + { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + + if(systemReadJoypads()) + { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) + { + if(gbSgbFourPlayers) + { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } + else + { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } + else + { + gbJoymask[0] = systemReadJoypad(-1); + } + } + int newmask = gbJoymask[0] & 255; + + if(gbRom[0x147] == 0x22) + { + systemUpdateMotionSensor(); + } + + if(newmask) + { + gbInterrupt |= 16; + } + + newmask = (gbJoymask[0] >> 10); + + speedup = (newmask & 1) ? true : false; + gbCapture = (newmask & 2) ? true : false; + + if(gbCapture && !gbCapturePrevious) + { + gbCaptureNumber++; + systemScreenCapture(gbCaptureNumber); + } + gbCapturePrevious = gbCapture; + + if(gbFrameSkipCount >= framesToSkip) + { + systemDrawScreen(); + gbFrameSkipCount = 0; + } + else + gbFrameSkipCount++; + } + else + { + // go the the OAM being accessed mode + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdMode = 2; + + // only one LCD interrupt per line. may need to generalize... + if(!(register_STAT & 0x40) || + (register_LY != register_LYC)) + { + if((register_STAT & 0x28) == 0x20) + gbInterrupt |= 2; + } + } + break; + case 1: + // V-Blank + // next mode is OAM being accessed mode + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdMode = 2; + if(!(register_STAT & 0x40) || + (register_LY != register_LYC)) + { + if((register_STAT & 0x28) == 0x20) + gbInterrupt |= 2; + } + break; + case 2: + // OAM being accessed mode + + // next mode is OAM and VRAM in use + gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS; + gbLcdMode = 3; + break; + case 3: + // OAM and VRAM in use + // next mode is H-Blank + if(register_LY < 144) + { + if(!gbSgbMask) + { + if(gbFrameSkipCount >= framesToSkip) + { + gbRenderLine(); + gbDrawSprites(); + + switch(systemColorDepth) + { + case 16: + { + u16 * dest = (u16 *)pix + + (gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160; ) + { + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + } + if(gbBorderOn) + dest += gbBorderColumnSkip; + *dest++ = 0; // for filters that read one pixel more + } + break; + case 24: + { + u8 *dest = (u8 *)pix + + 3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) + + gbBorderColumnSkip); + for(int x = 0; x < 160;) + { + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + } + } + break; + case 32: + { + u32 * dest = (u32 *)pix + + (gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160;) + { + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + } + } + break; + } + } + } + } + gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS; + gbLcdMode = 0; + // only one LCD interrupt per line. may need to generalize... + if(!(register_STAT & 0x40) || + (register_LY != register_LYC)) + { + if(register_STAT & 0x08) + gbInterrupt |= 2; + } + if(gbHdmaOn) + { + gbDoHdma(); + } + break; + } + // mark the correct lcd mode on STAT register + register_STAT = (register_STAT & 0xfc) | gbLcdMode; + } + } + + // serial emulation + if(gbSerialOn) + { +#ifdef LINK_EMULATION + if(linkConnected) + { + gbSerialTicks -= clockTicks; + + while(gbSerialTicks <= 0) + { + // increment number of shifted bits + gbSerialBits++; + linkProc(); + if(gbSerialOn && (gbMemory[0xff02] & 1)) + { + if(gbSerialBits == 8) + { + gbSerialBits = 0; + gbMemory[0xff01] = 0xff; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbInterrupt |= 8; + gbSerialTicks = 0; + } + } + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } + else + { +#endif + if(gbMemory[0xff02] & 1) + { + gbSerialTicks -= clockTicks; + + // overflow + while(gbSerialTicks <= 0) + { + // shift serial byte to right and put a 1 bit in its place + // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); + // increment number of shifted bits + gbSerialBits++; + if(gbSerialBits == 8) + { + // end of transmission + if(gbSerialFunction) // external device + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); + else + gbMemory[0xff01] = 0xff; + gbSerialTicks = 0; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbInterrupt |= 8; + gbSerialBits = 0; + } + else + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } +#ifdef LINK_EMULATION + } +#endif + } + + // timer emulation + if(gbTimerOn) + { + gbTimerTicks -= clockTicks; + + while(gbTimerTicks <= 0) + { + register_TIMA++; + + if(register_TIMA == 0) + { + // timer overflow! + + // reload timer modulo + register_TIMA = register_TMA; + + // flag interrupt + gbInterrupt |= 4; + } + + gbTimerTicks += gbTimerClockTicks; + } + } + + /* + if(soundOffFlag) { + if(synchronize && !speedup) { + synchronizeTicks -= clockTicks; + + while(synchronizeTicks < 0) { + synchronizeTicks += SYNCHRONIZE_CLOCK_TICKS; + + DWORD now = timeGetTime(); + gbElapsedTime += (now - timeNow); + + if(gbElapsedTime < 50) { + DWORD diff = 50 - gbElapsedTime; + Sleep(diff); + timeNow = timeGetTime(); + elapsedTime = timeNow - now - diff; + if((int)elapsedTime < 0) + elapsedTime = 0; + } else { + timeNow = timeGetTime(); + elapsedTime = 0; + } + } + } + } + */ + + soundTicks -= clockTicks; + + while(soundTicks < 0) + { + soundTicks += SOUND_CLOCK_TICKS; + + gbSoundTick(); + } + + register_IF = gbInterrupt; + + if(IFF & 0x20) + { + IFF &= 0xdf; + IFF |= 0x01; + gbInterruptWait = 0; + } + else if(gbInterrupt) + { + if(gbInterruptWait == 0) + { + // gbInterruptWait = 0; + + if(IFF & 0x01) + { + if((gbInterrupt & 1) && (register_IE & 1)) + { + gbVblank_interrupt(); + continue; + } + + if((gbInterrupt & 2) && (register_IE & 2)) + { + gbLcd_interrupt(); + continue; + } + + if((gbInterrupt & 4) && (register_IE & 4)) + { + gbTimer_interrupt(); + continue; + } + + if((gbInterrupt & 8) && (register_IE & 8)) + { + gbSerial_interrupt(); + continue; + } + + if((gbInterrupt & 16) && (register_IE & 16)) + { + gbJoypad_interrupt(); + continue; + } + } + } + else + { + gbInterruptWait -= clockTicks; + if(gbInterruptWait < 0) + gbInterruptWait = 0; + } + } + + if(ticksToStop <= 0) + { + if(!(register_LCDC & 0x80)) + { + if(systemReadJoypads()) + { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) + { + if(gbSgbFourPlayers) + { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } + else + { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } + else + { + gbJoymask[0] = systemReadJoypad(-1); + } + } + } + return; + } + } +} + +struct EmulatedSystem GBSystem = + { + // emuMain + gbEmulate, + // emuReset + gbReset, + // emuCleanUp + gbCleanUp, + // emuReadBattery + gbReadBatteryFile, + // emuWriteBattery + gbWriteBatteryFile, + // emuReadState + gbReadSaveState, + // emuWriteState + gbWriteSaveState, + // emuReadMemState + gbReadMemSaveState, + // emuWriteMemState + gbWriteMemSaveState, + // emuWritePNG + gbWritePNGFile, + // emuWriteBMP + gbWriteBMPFile, + // emuUpdateCPSR + NULL, + // emuHasDebugger + false, + // emuCount +#ifdef FINAL_VERSION + 70000/4, +#else + 1000, +#endif + }; diff --git a/src/gb/GB.h b/src/gb/GB.h new file mode 100644 index 0000000..92e7e08 --- /dev/null +++ b/src/gb/GB.h @@ -0,0 +1,62 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GB_GB_H +#define VBA_GB_GB_H + +#define C_FLAG 0x10 +#define H_FLAG 0x20 +#define N_FLAG 0x40 +#define Z_FLAG 0x80 + +typedef union { + struct + { +#ifdef WORDS_BIGENDIAN + u8 B1, B0; +#else + u8 B0,B1; +#endif + } + B; + u16 W; + } gbRegister; + +extern bool gbLoadRom(const char *); +extern void gbEmulate(int); +extern bool gbIsGameboyRom(const char *); +extern void gbSoundReset(); +extern void gbSoundSetQuality(int); +extern void gbReset(); +extern void gbCleanUp(); +extern bool gbWriteBatteryFile(const char *); +extern bool gbWriteBatteryFile(const char *, bool); +extern bool gbReadBatteryFile(const char *); +extern bool gbWriteSaveState(const char *); +extern bool gbWriteMemSaveState(char *, int); +extern bool gbReadSaveState(const char *); +extern bool gbReadMemSaveState(char *, int); +extern void gbSgbRenderBorder(); +extern bool gbWritePNGFile(const char *); +extern bool gbWriteBMPFile(const char *); +extern bool gbReadGSASnapshot(const char *); + +extern struct EmulatedSystem GBSystem; + +#endif diff --git a/src/gb/gbCheats.cpp b/src/gb/gbCheats.cpp new file mode 100644 index 0000000..c8d5a3d --- /dev/null +++ b/src/gb/gbCheats.cpp @@ -0,0 +1,498 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "sdfileio.h" +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "../Util.h" + +#include "gbCheats.h" +#include "gbGlobals.h" + +gbCheat gbCheatList[100]; +int gbCheatNumber = 0; +bool gbCheatMap[0x10000]; + +extern bool cheatsEnabled; + +#define GBCHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9')) +#define GBCHEAT_HEX_VALUE(a) ( (a) >= 'A' ? (a) - 'A' + 10 : (a) - '0') + +void gbCheatUpdateMap() +{ + memset(gbCheatMap, 0, 0x10000); + + for(int i = 0; i < gbCheatNumber; i++) + { + if(gbCheatList[i].enabled) + gbCheatMap[gbCheatList[i].address] = true; + } +} + +void gbCheatsSaveGame(gzFile gzFile) +{ + utilWriteInt(gzFile, gbCheatNumber); + if(gbCheatNumber) + utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); +} + +void gbCheatsReadGame(gzFile gzFile, int version) +{ + if(version <= 8) + { + int gbGgOn = utilReadInt(gzFile); + + if(gbGgOn) + { + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for(int i = 0; i < n; i++) + { + utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); + gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } + } + + int gbGsOn = utilReadInt(gzFile); + + if(gbGsOn) + { + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for(int i = 0; i < n; i++) + { + utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); + gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } + } + } + else + { + gbCheatNumber = utilReadInt(gzFile); + + if(gbCheatNumber) + { + utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); + } + } + + gbCheatUpdateMap(); +} + +void gbCheatsSaveCheatList(const char *file) +{ + if(gbCheatNumber == 0) + return; + FILE* f = gen_fopen(file, "wb"); + if(f == NULL) + return; + int version = 1; + gen_fwrite(&version, 1, sizeof(version), f); + int type = 1; + gen_fwrite(&type, 1, sizeof(type), f); + gen_fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f); + gen_fwrite(gbCheatList, 1, sizeof(gbCheatList), f); + gen_fclose(f); +} + +bool gbCheatsLoadCheatList(const char *file) +{ + gbCheatNumber = 0; + + gbCheatUpdateMap(); + + int count = 0; + + FILE* f = gen_fopen(file, "rb"); + + if(f == NULL) + return false; + + int version = 0; + + if(gen_fread(&version, 1, sizeof(version), f) != sizeof(version)) + { + gen_fclose(f); + return false; + } + + if(version != 1) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, + N_("Unsupported cheat list version %d"), version); + gen_fclose(f); + return false; + } + + int type = 0; + if(gen_fread(&type, 1, sizeof(type), f) != sizeof(type)) + { + gen_fclose(f); + return false; + } + + if(type != 1) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, + N_("Unsupported cheat list type %d"), type); + gen_fclose(f); + return false; + } + + if(gen_fread(&count, 1, sizeof(count), f) != sizeof(count)) + { + gen_fclose(f); + return false; + } + + if(gen_fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) + { + gen_fclose(f); + return false; + } + + gbCheatNumber = count; + gbCheatUpdateMap(); + + return true; +} + +bool gbVerifyGsCode(const char *code) +{ + int len = strlen(code); + + if(len == 0) + return true; + + if(len != 8) + return false; + + for(int i = 0; i < 8; i++) + if(!GBCHEAT_IS_HEX(code[i])) + return false; + + int address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]); + + if(address < 0xa000 || + address > 0xdfff) + return false; + + return true; +} + +void gbAddGsCheat(const char *code, const char *desc) +{ + if(gbCheatNumber > 99) + { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return; + } + + if(!gbVerifyGsCode(code)) + { + systemMessage(MSG_INVALID_GAMESHARK_CODE, + N_("Invalid GameShark code: %s"), code); + return; + } + + int i = gbCheatNumber; + + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); + + gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 | + GBCHEAT_HEX_VALUE(code[1]); + + gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 | + GBCHEAT_HEX_VALUE(code[3]); + + gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]); + + gbCheatList[i].compare = 0; + + gbCheatList[i].enabled = true; + + gbCheatMap[gbCheatList[i].address] = true; + + gbCheatNumber++; +} + +bool gbVerifyGgCode(const char *code) +{ + int len = strlen(code); + + if(len != 11 && + len != 7 && + len != 6 && + len != 0) + return false; + + if(len == 0) + return true; + + if(!GBCHEAT_IS_HEX(code[0])) + return false; + if(!GBCHEAT_IS_HEX(code[1])) + return false; + if(!GBCHEAT_IS_HEX(code[2])) + return false; + if(code[3] != '-') + return false; + if(!GBCHEAT_IS_HEX(code[4])) + return false; + if(!GBCHEAT_IS_HEX(code[5])) + return false; + if(!GBCHEAT_IS_HEX(code[6])) + return false; + if(code[7] != 0) + { + if(code[7] != '-') + return false; + if(code[8] != 0) + { + if(!GBCHEAT_IS_HEX(code[8])) + return false; + if(!GBCHEAT_IS_HEX(code[9])) + return false; + if(!GBCHEAT_IS_HEX(code[10])) + return false; + } + } + + // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + // GBCHEAT_HEX_VALUE(code[1]); + + int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + + if(address >= 0x8000 && address <= 0x9fff) + return false; + + if(address >= 0xc000) + return false; + + if(code[7] == 0 || code[8] == '0') + return true; + + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ( (compare << 6) & 0xc0); + compare ^= 0x45; + + int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9])); + + if(cloak >=1 && cloak <= 7) + return false; + + return true; +} + +void gbAddGgCheat(const char *code, const char *desc) +{ + if(gbCheatNumber > 99) + { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return; + } + + if(!gbVerifyGgCode(code)) + { + systemMessage(MSG_INVALID_GAMEGENIE_CODE, + N_("Invalid GameGenie code: %s"), code); + return; + } + + int i = gbCheatNumber; + + int len = strlen(code); + + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); + + gbCheatList[i].code = 1; + gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + GBCHEAT_HEX_VALUE(code[1]); + + gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + + gbCheatList[i].compare = 0; + + if(len != 7 && len != 8) + { + + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ( (compare << 6) & 0xc0); + compare ^= 0x45; + + gbCheatList[i].compare = compare; + gbCheatList[i].code = 0; + } + + gbCheatList[i].enabled = true; + + gbCheatMap[gbCheatList[i].address] = true; + + gbCheatNumber++; +} + +void gbCheatRemove(int i) +{ + if(i < 0 || i >= gbCheatNumber) + { + systemMessage(MSG_INVALID_CHEAT_TO_REMOVE, + N_("Invalid cheat to remove %d"), i); + return; + } + + if((i+1) < gbCheatNumber) + { + memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)* + (gbCheatNumber-i-1)); + } + + gbCheatNumber--; + + gbCheatUpdateMap(); +} + +void gbCheatRemoveAll() +{ + gbCheatNumber = 0; + gbCheatUpdateMap(); +} + +void gbCheatEnable(int i) +{ + if(i >=0 && i < gbCheatNumber) + { + if(!gbCheatList[i].enabled) + { + gbCheatList[i].enabled = true; + gbCheatUpdateMap(); + } + } +} + +void gbCheatDisable(int i) +{ + if(i >=0 && i < gbCheatNumber) + { + if(gbCheatList[i].enabled) + { + gbCheatList[i].enabled = false; + gbCheatUpdateMap(); + } + } +} + +bool gbCheatReadGSCodeFile(const char *fileName) +{ + FILE* file = gen_fopen(fileName, "rb"); + + if(!file) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + gen_fseek(file, 0x18, SEEK_SET); + int count = 0; + gen_fread(&count, 1, 2, file); + int dummy = 0; + gbCheatRemoveAll(); + char desc[13]; + char code[9]; + int i; + for(i = 0; i < count; i++) + { + gen_fread(&dummy, 1, 2, file); + gen_fread(desc, 1, 12, file); + desc[12] = 0; + gen_fread(code, 1, 8, file); + code[8] = 0; + gbAddGsCheat(code, desc); + } + + for(i = 0; i < gbCheatNumber; i++) + gbCheatDisable(i); + + gen_fclose(file); + return true; +} + +u8 gbCheatRead(u16 address) +{ + if(!cheatsEnabled) + return gbMemoryMap[address>>12][address & 0xFFF]; + + for(int i = 0; i < gbCheatNumber; i++) + { + if(gbCheatList[i].enabled && gbCheatList[i].address == address) + { + switch(gbCheatList[i].code) + { + case 0x100: // GameGenie support + if(gbMemoryMap[address>>12][address&0xFFF] == gbCheatList[i].compare) + return gbCheatList[i].value; + break; + case 0x00: + case 0x01: + case 0x80: + return gbCheatList[i].value; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + if(address >= 0xd000 && address < 0xe000) + { + if(((gbMemoryMap[0x0d] - gbWram)/0x1000) == + (gbCheatList[i].code - 0x90)) + return gbCheatList[i].value; + } + else + return gbCheatList[i].value; + } + } + } + return gbMemoryMap[address>>12][address&0xFFF]; +} diff --git a/src/gb/gbCheats.h b/src/gb/gbCheats.h new file mode 100644 index 0000000..226d1b8 --- /dev/null +++ b/src/gb/gbCheats.h @@ -0,0 +1,60 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef __VBA_GB_GBCHEATS_H +#define __VBA_GB_GBCHEATS_H + +#include "../System.h" + +struct gbXxCheat + { + char cheatDesc[100]; + char cheatCode[20]; + }; + +struct gbCheat + { + char cheatCode[20]; + char cheatDesc[32]; + u16 address; + int code; + u8 compare; + u8 value; + bool enabled; + }; + +extern void gbCheatsSaveGame(gzFile); +extern void gbCheatsReadGame(gzFile, int); +extern void gbCheatsSaveCheatList(const char *); +extern bool gbCheatsLoadCheatList(const char *); +extern bool gbCheatReadGSCodeFile(const char *); + +extern void gbAddGsCheat(const char *, const char*); +extern void gbAddGgCheat(const char *, const char*); +extern void gbCheatRemove(int); +extern void gbCheatRemoveAll(); +extern void gbCheatEnable(int); +extern void gbCheatDisable(int); +extern u8 gbCheatRead(u16); + +extern int gbCheatNumber; +extern gbCheat gbCheatList[100]; +extern bool gbCheatMap[0x10000]; +#endif + diff --git a/src/gb/gbCodes.h b/src/gb/gbCodes.h new file mode 100644 index 0000000..387aeaf --- /dev/null +++ b/src/gb/gbCodes.h @@ -0,0 +1,1445 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +case 0x00: +// NOP +break; +case 0x01: +// LD BC, NNNN +BC.B.B0=gbReadMemory(PC.W++); +BC.B.B1=gbReadMemory(PC.W++); +break; +case 0x02: +// LD (BC),A +gbWriteMemory(BC.W,AF.B.B1); +break; +case 0x03: +// INC BC +BC.W++; +break; +case 0x04: +// INC B +BC.B.B1++; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B1]| (BC.B.B1&0x0F? 0:H_FLAG); +break; +case 0x05: +// DEC B +BC.B.B1--; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B1]| + ((BC.B.B1&0x0F)==0x0F? H_FLAG:0); +break; +case 0x06: +// LD B, NN +BC.B.B1=gbReadOpcode(PC.W++); +break; +case 0x07: +// RLCA +tempValue=AF.B.B1&0x80? C_FLAG:0; +AF.B.B1=(AF.B.B1<<1)|(AF.B.B1>>7); +AF.B.B0=tempValue; +break; +case 0x08: +// LD (NNNN), SP +tempRegister.B.B0=gbReadOpcode(PC.W++); +tempRegister.B.B1=gbReadOpcode(PC.W++); +gbWriteMemory(tempRegister.W++,SP.B.B0); +gbWriteMemory(tempRegister.W,SP.B.B1); +break; +case 0x09: +// ADD HL,BC +tempRegister.W=(HL.W+BC.W)&0xFFFF; +AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^BC.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)BC.W)&0x10000? C_FLAG:0); +HL.W=tempRegister.W; +break; +case 0x0a: +// LD A,(BC) +AF.B.B1=gbReadMemory(BC.W); +break; +case 0x0b: +// DEC BC +BC.W--; +break; +case 0x0c: +// INC C +BC.B.B0++; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B0]| (BC.B.B0&0x0F? 0:H_FLAG); +break; +case 0x0d: +// DEC C +BC.B.B0--; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B0]| + ((BC.B.B0&0x0F)==0x0F? H_FLAG:0); +break; +case 0x0e: +// LD C, NN +BC.B.B0=gbReadOpcode(PC.W++); +break; +case 0x0f: +// RRCA +tempValue=AF.B.B1&0x01; +AF.B.B1=(AF.B.B1>>1)|(tempValue? 0x80:0); +AF.B.B0=(tempValue<<4); +break; +case 0x10: +// STOP +opcode = gbReadOpcode(PC.W++); +if(gbCgbMode) + { + if(gbMemory[0xff4d] & 1) + { + gbSpeedSwitch(); + + if(gbSpeed == 0) + gbMemory[0xff4d] = 0x00; + else + gbMemory[0xff4d] = 0x80; + } + } +break; +case 0x11: +// LD DE, NNNN +DE.B.B0=gbReadMemory(PC.W++); +DE.B.B1=gbReadMemory(PC.W++); +break; +case 0x12: +// LD (DE),A +gbWriteMemory(DE.W,AF.B.B1); +break; +case 0x13: +// INC DE +DE.W++; +break; +case 0x14: +// INC D +DE.B.B1++; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B1]| (DE.B.B1&0x0F? 0:H_FLAG); +break; +case 0x15: +// DEC D +DE.B.B1--; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B1]| + ((DE.B.B1&0x0F)==0x0F? H_FLAG:0); +break; +case 0x16: +// LD D,NN +DE.B.B1=gbReadOpcode(PC.W++); +break; +case 0x17: +// RLA +tempValue=AF.B.B1&0x80? C_FLAG:0; +AF.B.B1=(AF.B.B1<<1)|((AF.B.B0&C_FLAG)>>4); +AF.B.B0=tempValue; +break; +case 0x18: +// JR NN +PC.W+=(s8)gbReadMemory(PC.W)+1; +break; +case 0x19: +// ADD HL,DE +tempRegister.W=(HL.W+DE.W)&0xFFFF; +AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^DE.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)DE.W)&0x10000? C_FLAG:0); +HL.W=tempRegister.W; +break; +case 0x1a: +// LD A,(DE) +AF.B.B1=gbReadMemory(DE.W); +break; +case 0x1b: +// DEC DE +DE.W--; +break; +case 0x1c: +// INC E +DE.B.B0++; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B0]| (DE.B.B0&0x0F? 0:H_FLAG); +break; +case 0x1d: +// DEC E +DE.B.B0--; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B0]| + ((DE.B.B0&0x0F)==0x0F? H_FLAG:0); +break; +case 0x1e: +// LD E,NN +DE.B.B0=gbReadOpcode(PC.W++); +break; +case 0x1f: +// RRA +tempValue=AF.B.B1&0x01; +AF.B.B1=(AF.B.B1>>1)|(AF.B.B0&C_FLAG? 0x80:0); +AF.B.B0=(tempValue<<4); +break; +case 0x20: +// JR NZ,NN +if(AF.B.B0&Z_FLAG) + PC.W++; +else + { + PC.W+=(s8)gbReadMemory(PC.W)+1; + clockTicks++; + } +break; +case 0x21: +// LD HL,NNNN +HL.B.B0=gbReadMemory(PC.W++); +HL.B.B1=gbReadMemory(PC.W++); +break; +case 0x22: +// LDI (HL),A +gbWriteMemory(HL.W++,AF.B.B1); +break; +case 0x23: +// INC HL +HL.W++; +break; +case 0x24: +// INC H +HL.B.B1++; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B1]| (HL.B.B1&0x0F? 0:H_FLAG); +break; +case 0x25: +// DEC H +HL.B.B1--; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B1]| + ((HL.B.B1&0x0F)==0x0F? H_FLAG:0); +break; +case 0x26: +// LD H,NN +HL.B.B1=gbReadOpcode(PC.W++); +break; +case 0x27: +// DAA +tempRegister.W=AF.B.B1; +if(AF.B.B0&C_FLAG) tempRegister.W|=256; +if(AF.B.B0&H_FLAG) tempRegister.W|=512; +if(AF.B.B0&N_FLAG) tempRegister.W|=1024; +AF.W=DAATable[tempRegister.W]; +break; +case 0x28: +// JR Z,NN +if(AF.B.B0&Z_FLAG) + { + PC.W+=(s8)gbReadMemory(PC.W)+1; + clockTicks++; + } +else + PC.W++; +break; +case 0x29: +// ADD HL,HL +tempRegister.W=(HL.W+HL.W)&0xFFFF; +AF.B.B0= (AF.B.B0 & Z_FLAG)| + ((HL.W^HL.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)HL.W)&0x10000? C_FLAG:0); +HL.W=tempRegister.W; +break; +case 0x2a: +// LDI A,(HL) +AF.B.B1 = gbReadMemory(HL.W++); +break; +case 0x2b: +// DEC HL +HL.W--; +break; +case 0x2c: +// INC L +HL.B.B0++; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B0]| (HL.B.B0&0x0F? 0:H_FLAG); +break; +case 0x2d: +// DEC L +HL.B.B0--; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B0]| + ((HL.B.B0&0x0F)==0x0F? H_FLAG:0); +break; +case 0x2e: +// LD L,NN +HL.B.B0=gbReadOpcode(PC.W++); +break; +case 0x2f: +// CPL +AF.B.B1 ^= 255; +AF.B.B0|=N_FLAG|H_FLAG; +break; +case 0x30: +// JR NC,NN +if(AF.B.B0&C_FLAG) + PC.W++; +else + { + PC.W+=(s8)gbReadMemory(PC.W)+1; + clockTicks++; + } +break; +case 0x31: +// LD SP,NNNN +SP.B.B0=gbReadMemory(PC.W++); +SP.B.B1=gbReadMemory(PC.W++); +break; +case 0x32: +// LDD (HL),A +gbWriteMemory(HL.W--,AF.B.B1); +break; +case 0x33: +// INC SP +SP.W++; +break; +case 0x34: +// INC (HL) +tempValue=gbReadMemory(HL.W)+1; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[tempValue]| (tempValue&0x0F? 0:H_FLAG); +gbWriteMemory(HL.W,tempValue); +break; +case 0x35: +// DEC (HL) +tempValue=gbReadMemory(HL.W)-1; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[tempValue]| + ((tempValue&0x0F)==0x0F? H_FLAG:0); +gbWriteMemory(HL.W,tempValue); +break; +case 0x36: +// LD (HL),NN +gbWriteMemory(HL.W,gbReadOpcode(PC.W++)); +break; +case 0x37: +// SCF +AF.B.B0 = AF.B.B0 & Z_FLAG | C_FLAG; +break; +case 0x38: +// JR C,NN +if(AF.B.B0&C_FLAG) + { + PC.W+=(s8)gbReadMemory(PC.W)+1; + clockTicks ++; + } +else + PC.W++; +break; +case 0x39: +// ADD HL,SP +tempRegister.W=(HL.W+SP.W)&0xFFFF; +AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^SP.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)SP.W)&0x10000? C_FLAG:0); +HL.W=tempRegister.W; +break; +case 0x3a: +// LDD A,(HL) +AF.B.B1 = gbReadMemory(HL.W--); +break; +case 0x3b: +// DEC SP +SP.W--; +break; +case 0x3c: +// INC A +AF.B.B1++; +AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[AF.B.B1]| (AF.B.B1&0x0F? 0:H_FLAG); +break; +case 0x3d: +// DEC A +AF.B.B1--; +AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[AF.B.B1]| + ((AF.B.B1&0x0F)==0x0F? H_FLAG:0); +break; +case 0x3e: +// LD A,NN +AF.B.B1=gbReadOpcode(PC.W++); +break; +case 0x3f: +// CCF +AF.B.B0^=C_FLAG; +AF.B.B0&=~(N_FLAG|H_FLAG); +break; +case 0x40: +// LD B,B +BC.B.B1=BC.B.B1; +break; +case 0x41: +// LD B,C +BC.B.B1=BC.B.B0; +break; +case 0x42: +// LD B,D +BC.B.B1=DE.B.B1; +break; +case 0x43: +// LD B,E +BC.B.B1=DE.B.B0; +break; +case 0x44: +// LD B,H +BC.B.B1=HL.B.B1; +break; +case 0x45: +// LD B,L +BC.B.B1=HL.B.B0; +break; +case 0x46: +// LD B,(HL) +BC.B.B1=gbReadMemory(HL.W); +break; +case 0x47: +// LD B,A +BC.B.B1=AF.B.B1; +break; +case 0x48: +// LD C,B +BC.B.B0=BC.B.B1; +break; +case 0x49: +// LD C,C +BC.B.B0=BC.B.B0; +break; +case 0x4a: +// LD C,D +BC.B.B0=DE.B.B1; +break; +case 0x4b: +// LD C,E +BC.B.B0=DE.B.B0; +break; +case 0x4c: +// LD C,H +BC.B.B0=HL.B.B1; +break; +case 0x4d: +// LD C,L +BC.B.B0=HL.B.B0; +break; +case 0x4e: +// LD C,(HL) +BC.B.B0=gbReadMemory(HL.W); +break; +case 0x4f: +// LD C,A +BC.B.B0=AF.B.B1; +break; +case 0x50: +// LD D,B +DE.B.B1=BC.B.B1; +break; +case 0x51: +// LD D,C +DE.B.B1=BC.B.B0; +break; +case 0x52: +// LD D,D +DE.B.B1=DE.B.B1; +break; +case 0x53: +// LD D,E +DE.B.B1=DE.B.B0; +break; +case 0x54: +// LD D,H +DE.B.B1=HL.B.B1; +break; +case 0x55: +// LD D,L +DE.B.B1=HL.B.B0; +break; +case 0x56: +// LD D,(HL) +DE.B.B1=gbReadMemory(HL.W); +break; +case 0x57: +// LD D,A +DE.B.B1=AF.B.B1; +break; +case 0x58: +// LD E,B +DE.B.B0=BC.B.B1; +break; +case 0x59: +// LD E,C +DE.B.B0=BC.B.B0; +break; +case 0x5a: +// LD E,D +DE.B.B0=DE.B.B1; +break; +case 0x5b: +// LD E,E +DE.B.B0=DE.B.B0; +break; +case 0x5c: +// LD E,H +DE.B.B0=HL.B.B1; +break; +case 0x5d: +// LD E,L +DE.B.B0=HL.B.B0; +break; +case 0x5e: +// LD E,(HL) +DE.B.B0=gbReadMemory(HL.W); +break; +case 0x5f: +// LD E,A +DE.B.B0=AF.B.B1; +break; +case 0x60: +// LD H,B +HL.B.B1=BC.B.B1; +break; +case 0x61: +// LD H,C +HL.B.B1=BC.B.B0; +break; +case 0x62: +// LD H,D +HL.B.B1=DE.B.B1; +break; +case 0x63: +// LD H,E +HL.B.B1=DE.B.B0; +break; +case 0x64: +// LD H,H +HL.B.B1=HL.B.B1; +break; +case 0x65: +// LD H,L +HL.B.B1=HL.B.B0; +break; +case 0x66: +// LD H,(HL) +HL.B.B1=gbReadMemory(HL.W); +break; +case 0x67: +// LD H,A +HL.B.B1=AF.B.B1; +break; +case 0x68: +// LD L,B +HL.B.B0=BC.B.B1; +break; +case 0x69: +// LD L,C +HL.B.B0=BC.B.B0; +break; +case 0x6a: +// LD L,D +HL.B.B0=DE.B.B1; +break; +case 0x6b: +// LD L,E +HL.B.B0=DE.B.B0; +break; +case 0x6c: +// LD L,H +HL.B.B0=HL.B.B1; +break; +case 0x6d: +// LD L,L +HL.B.B0=HL.B.B0; +break; +case 0x6e: +// LD L,(HL) +HL.B.B0=gbReadMemory(HL.W); +break; +case 0x6f: +// LD L,A +HL.B.B0=AF.B.B1; +break; +case 0x70: +// LD (HL),B +gbWriteMemory(HL.W,BC.B.B1); +break; +case 0x71: +// LD (HL),C +gbWriteMemory(HL.W,BC.B.B0); +break; +case 0x72: +// LD (HL),D +gbWriteMemory(HL.W,DE.B.B1); +break; +case 0x73: +// LD (HL),E +gbWriteMemory(HL.W,DE.B.B0); +break; +case 0x74: +// LD (HL),H +gbWriteMemory(HL.W,HL.B.B1); +break; +case 0x75: +// LD (HL),L +gbWriteMemory(HL.W,HL.B.B0); +break; +case 0x76: +// HALT +if(IFF & 1) + { + PC.W--; + IFF |= 0x80; + } +else + { + if((register_IE & register_IF) > 0) + IFF |= 0x100; + else + { + PC.W--; + IFF |= 0x81; + } + } +break; +case 0x77: +// LD (HL),A +gbWriteMemory(HL.W,AF.B.B1); +break; +case 0x78: +// LD A,B +AF.B.B1=BC.B.B1; +break; +case 0x79: +// LD A,C +AF.B.B1=BC.B.B0; +break; +case 0x7a: +// LD A,D +AF.B.B1=DE.B.B1; +break; +case 0x7b: +// LD A,E +AF.B.B1=DE.B.B0; +break; +case 0x7c: +// LD A,H +AF.B.B1=HL.B.B1; +break; +case 0x7d: +// LD A,L +AF.B.B1=HL.B.B0; +break; +case 0x7e: +// LD A,(HL) +AF.B.B1=gbReadMemory(HL.W); +break; +case 0x7f: +// LD A,A +AF.B.B1=AF.B.B1; +break; +case 0x80: +// ADD B +tempRegister.W=AF.B.B1+BC.B.B1; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x81: +// ADD C +tempRegister.W=AF.B.B1+BC.B.B0; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x82: +// ADD D +tempRegister.W=AF.B.B1+DE.B.B1; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x83: +// ADD E +tempRegister.W=AF.B.B1+DE.B.B0; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x84: +// ADD H +tempRegister.W=AF.B.B1+HL.B.B1; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x85: +// ADD L +tempRegister.W=AF.B.B1+HL.B.B0; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x86: +// ADD (HL) +tempValue=gbReadMemory(HL.W); +tempRegister.W=AF.B.B1+tempValue; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x87: +// ADD A +tempRegister.W=AF.B.B1+AF.B.B1; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x88: +// ADC B: +tempRegister.W=AF.B.B1+BC.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x89: +// ADC C +tempRegister.W=AF.B.B1+BC.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x8a: +// ADC D +tempRegister.W=AF.B.B1+DE.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x8b: +// ADC E +tempRegister.W=AF.B.B1+DE.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x8c: +// ADC H +tempRegister.W=AF.B.B1+HL.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x8d: +// ADC L +tempRegister.W=AF.B.B1+HL.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x8e: +// ADC (HL) +tempValue=gbReadMemory(HL.W); +tempRegister.W=AF.B.B1+tempValue+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x8f: +// ADC A +tempRegister.W=AF.B.B1+AF.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x90: +// SUB B +tempRegister.W=AF.B.B1-BC.B.B1; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x91: +// SUB C +tempRegister.W=AF.B.B1-BC.B.B0; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x92: +// SUB D +tempRegister.W=AF.B.B1-DE.B.B1; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x93: +// SUB E +tempRegister.W=AF.B.B1-DE.B.B0; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x94: +// SUB H +tempRegister.W=AF.B.B1-HL.B.B1; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x95: +// SUB L +tempRegister.W=AF.B.B1-HL.B.B0; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x96: +// SUB (HL) +tempValue=gbReadMemory(HL.W); +tempRegister.W=AF.B.B1-tempValue; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x97: +// SUB A +AF.B.B1=0; +AF.B.B0=N_FLAG|Z_FLAG; +break; +case 0x98: +// SBC B +tempRegister.W=AF.B.B1-BC.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x99: +// SBC C +tempRegister.W=AF.B.B1-BC.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x9a: +// SBC D +tempRegister.W=AF.B.B1-DE.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x9b: +// SBC E +tempRegister.W=AF.B.B1-DE.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x9c: +// SBC H +tempRegister.W=AF.B.B1-HL.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x9d: +// SBC L +tempRegister.W=AF.B.B1-HL.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x9e: +// SBC (HL) +tempValue=gbReadMemory(HL.W); +tempRegister.W=AF.B.B1-tempValue-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0x9f: +// SBC A +tempRegister.W=AF.B.B1-AF.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0xa0: +// AND B +AF.B.B1&=BC.B.B1; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa1: +// AND C +AF.B.B1&=BC.B.B0; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa2: +// AND_D +AF.B.B1&=DE.B.B1; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa3: +// AND E +AF.B.B1&=DE.B.B0; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa4: +// AND H +AF.B.B1&=HL.B.B1; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa5: +// AND L +AF.B.B1&=HL.B.B0; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa6: +// AND (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B1&=tempValue; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa7: +// AND A +AF.B.B1&=AF.B.B1; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xa8: +// XOR B +AF.B.B1^=BC.B.B1; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xa9: +// XOR C +AF.B.B1^=BC.B.B0; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xaa: +// XOR D +AF.B.B1^=DE.B.B1; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xab: +// XOR E +AF.B.B1^=DE.B.B0; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xac: +// XOR H +AF.B.B1^=HL.B.B1; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xad: +// XOR L +AF.B.B1^=HL.B.B0; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xae: +// XOR (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B1^=tempValue; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xaf: +// XOR A +AF.B.B1=0; +AF.B.B0=Z_FLAG; +break; +case 0xb0: +// OR B +AF.B.B1|=BC.B.B1; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb1: +// OR C +AF.B.B1|=BC.B.B0; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb2: +// OR D +AF.B.B1|=DE.B.B1; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb3: +// OR E +AF.B.B1|=DE.B.B0; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb4: +// OR H +AF.B.B1|=HL.B.B1; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb5: +// OR L +AF.B.B1|=HL.B.B0; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb6: +// OR (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B1|=tempValue; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb7: +// OR A +AF.B.B1|=AF.B.B1; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xb8: +// CP B: +tempRegister.W=AF.B.B1-BC.B.B1; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xb9: +// CP C +tempRegister.W=AF.B.B1-BC.B.B0; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xba: +// CP D +tempRegister.W=AF.B.B1-DE.B.B1; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xbb: +// CP E +tempRegister.W=AF.B.B1-DE.B.B0; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xbc: +// CP H +tempRegister.W=AF.B.B1-HL.B.B1; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xbd: +// CP L +tempRegister.W=AF.B.B1-HL.B.B0; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xbe: +// CP (HL) +tempValue=gbReadMemory(HL.W); +tempRegister.W=AF.B.B1-tempValue; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xbf: +// CP A +AF.B.B0=N_FLAG|Z_FLAG; +break; +case 0xc0: +// RET NZ +if(!(AF.B.B0&Z_FLAG)) + { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } +break; +case 0xc1: +// POP BC +BC.B.B0=gbReadMemory(SP.W++); +BC.B.B1=gbReadMemory(SP.W++); +break; +case 0xc2: +// JP NZ,NNNN +if(AF.B.B0&Z_FLAG) + PC.W+=2; +else + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } +break; +case 0xc3: +// JP NNNN +tempRegister.B.B0=gbReadMemory(PC.W++); +tempRegister.B.B1=gbReadMemory(PC.W); +PC.W=tempRegister.W; +break; +case 0xc4: +// CALL NZ,NNNN +if(AF.B.B0&Z_FLAG) + PC.W+=2; +else + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } +break; +case 0xc5: +// PUSH BC +gbWriteMemory(--SP.W,BC.B.B1); +gbWriteMemory(--SP.W,BC.B.B0); +break; +case 0xc6: +// ADD NN +tempValue=gbReadOpcode(PC.W++); +tempRegister.W=AF.B.B1+tempValue; +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10 ? H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0xc7: +// RST 00 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0000; +break; +case 0xc8: +// RET Z +if(AF.B.B0&Z_FLAG) + { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } +break; +case 0xc9: +// RET +PC.B.B0=gbReadMemory(SP.W++); +PC.B.B1=gbReadMemory(SP.W++); +break; +case 0xca: +// JP Z,NNNN +if(AF.B.B0&Z_FLAG) + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } +else + PC.W+=2; +break; +// CB done outside +case 0xcc: +// CALL Z,NNNN +if(AF.B.B0&Z_FLAG) + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } +else + PC.W+=2; +break; +case 0xcd: +// CALL NNNN +tempRegister.B.B0=gbReadMemory(PC.W++); +tempRegister.B.B1=gbReadMemory(PC.W++); +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=tempRegister.W; +break; +case 0xce: +// ADC NN +tempValue=gbReadOpcode(PC.W++); +tempRegister.W=AF.B.B1+tempValue+(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0xcf: +// RST 08 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0008; +break; +case 0xd0: +// RET NC +if(!(AF.B.B0&C_FLAG)) + { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } +break; +case 0xd1: +// POP DE +DE.B.B0=gbReadMemory(SP.W++); +DE.B.B1=gbReadMemory(SP.W++); +break; +case 0xd2: +// JP NC,NNNN +if(AF.B.B0&C_FLAG) + PC.W+=2; +else + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } +break; +// D3 illegal +case 0xd4: +// CALL NC,NNNN +if(AF.B.B0&C_FLAG) + PC.W+=2; +else + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } +break; +case 0xd5: +// PUSH DE +gbWriteMemory(--SP.W,DE.B.B1); +gbWriteMemory(--SP.W,DE.B.B0); +break; +case 0xd6: +// SUB NN +tempValue=gbReadOpcode(PC.W++); +tempRegister.W=AF.B.B1-tempValue; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0xd7: +// RST 10 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0010; +break; +case 0xd8: +// RET C +if(AF.B.B0&C_FLAG) + { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 4; + } +break; +case 0xd9: +// RETI +PC.B.B0=gbReadMemory(SP.W++); +PC.B.B1=gbReadMemory(SP.W++); +IFF |= 0x01; +break; +case 0xda: +// JP C,NNNN +if(AF.B.B0&C_FLAG) + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } +else + PC.W+=2; +break; +// DB illegal +case 0xdc: +// CALL C,NNNN +if(AF.B.B0&C_FLAG) + { + tempRegister.B.B0=gbReadMemory(PC.W++); + tempRegister.B.B1=gbReadMemory(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } +else + PC.W+=2; +break; +// DD illegal +case 0xde: +// SBC NN +tempValue=gbReadOpcode(PC.W++); +tempRegister.W=AF.B.B1-tempValue-(AF.B.B0&C_FLAG ? 1 : 0); +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +AF.B.B1=tempRegister.B.B0; +break; +case 0xdf: +// RST 18 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0018; +break; +case 0xe0: +// LD (FF00+NN),A +gbWriteMemory(0xff00 + gbReadOpcode(PC.W++),AF.B.B1); +break; +case 0xe1: +// POP HL +HL.B.B0=gbReadMemory(SP.W++); +HL.B.B1=gbReadMemory(SP.W++); +break; +case 0xe2: +// LD (FF00+C),A +gbWriteMemory(0xff00 + BC.B.B0,AF.B.B1); +break; +// E3 illegal +// E4 illegal +case 0xe5: +// PUSH HL +gbWriteMemory(--SP.W,HL.B.B1); +gbWriteMemory(--SP.W,HL.B.B0); +break; +case 0xe6: +// AND NN +tempValue=gbReadOpcode(PC.W++); +AF.B.B1&=tempValue; +AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; +break; +case 0xe7: +// RST 20 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0020; +break; +case 0xe8: +// ADD SP,NN +offset = (s8)gbReadOpcode(PC.W++); + +if(offset >= 0) + { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W > tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000? H_FLAG:0); + SP.W = tempRegister.W; + } +else + { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W < tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000?H_FLAG:0); + SP.W = tempRegister.W; + } +break; +case 0xe9: +// LD PC,HL +PC.W=HL.W; +break; +case 0xea: +// LD (NNNN),A +tempRegister.B.B0=gbReadOpcode(PC.W++); +tempRegister.B.B1=gbReadOpcode(PC.W++); +gbWriteMemory(tempRegister.W,AF.B.B1); +break; +// EB illegal +// EC illegal +// ED illegal +case 0xee: +// XOR NN +tempValue=gbReadOpcode(PC.W++); +AF.B.B1^=tempValue; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xef: +// RST 28 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0028; +break; +case 0xf0: +// LD A,(FF00+NN) +AF.B.B1 = gbReadMemory(0xff00+gbReadOpcode(PC.W++)); +break; +case 0xf1: +// POP AF +AF.B.B0=gbReadMemory(SP.W++); +AF.B.B1=gbReadMemory(SP.W++); +break; +case 0xf2: +// LD A,(FF00+C) +AF.B.B1 = gbReadMemory(0xff00+BC.B.B0); +break; +case 0xf3: +// DI +// IFF&=0xFE; +IFF&=(~0x21); +break; +// F4 illegal +case 0xf5: +// PUSH AF +gbWriteMemory(--SP.W,AF.B.B1); +gbWriteMemory(--SP.W,AF.B.B0); +break; +case 0xf6: +// OR NN +tempValue=gbReadOpcode(PC.W++); +AF.B.B1|=tempValue; +AF.B.B0=ZeroTable[AF.B.B1]; +break; +case 0xf7: +// RST 30 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0030; +break; +case 0xf8: +// LD HL,SP+NN +offset = (s8)gbReadOpcode(PC.W++); +if(offset >= 0) + { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W > tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000? H_FLAG:0); + HL.W = tempRegister.W; + } +else + { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W < tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000?H_FLAG:0); + HL.W = tempRegister.W; + } +break; +case 0xf9: +// LD SP,HL +SP.W=HL.W; +break; +case 0xfa: +// LD A,(NNNN) +tempRegister.B.B0=gbReadOpcode(PC.W++); +tempRegister.B.B1=gbReadOpcode(PC.W++); +AF.B.B1=gbReadMemory(tempRegister.W); +break; +case 0xfb: +// EI +IFF|=0x20; +break; +// FC illegal +// FD illegal +case 0xfe: +// CP NN +tempValue=gbReadOpcode(PC.W++); +tempRegister.W=AF.B.B1-tempValue; +AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); +break; +case 0xff: +// RST 38 +gbWriteMemory(--SP.W,PC.B.B1); +gbWriteMemory(--SP.W,PC.B.B0); +PC.W=0x0038; +break; +default: +systemMessage(0, N_("Unknown opcode %02x at %04x"), + gbReadOpcode(PC.W-1),PC.W-1); +emulating = false; +return; diff --git a/src/gb/gbCodesCB.h b/src/gb/gbCodesCB.h new file mode 100644 index 0000000..d16335a --- /dev/null +++ b/src/gb/gbCodesCB.h @@ -0,0 +1,1336 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +case 0x00: +// RLC B +AF.B.B0 = (BC.B.B1 & 0x80)?C_FLAG:0; +BC.B.B1 = (BC.B.B1<<1) | (BC.B.B1>>7); +AF.B.B0 |= ZeroTable[BC.B.B1]; +break; +case 0x01: +// RLC C +AF.B.B0 = (BC.B.B0 & 0x80)?C_FLAG:0; +BC.B.B0 = (BC.B.B0<<1) | (BC.B.B0>>7); +AF.B.B0 |= ZeroTable[BC.B.B0]; +break; +case 0x02: +// RLC D +AF.B.B0 = (DE.B.B1 & 0x80)?C_FLAG:0; +DE.B.B1 = (DE.B.B1<<1) | (DE.B.B1>>7); +AF.B.B0 |= ZeroTable[DE.B.B1]; +break; +case 0x03: +// RLC E +AF.B.B0 = (DE.B.B0 & 0x80)?C_FLAG:0; +DE.B.B0 = (DE.B.B0<<1) | (DE.B.B0>>7); +AF.B.B0 |= ZeroTable[DE.B.B0]; +break; +case 0x04: +// RLC H +AF.B.B0 = (HL.B.B1 & 0x80)?C_FLAG:0; +HL.B.B1 = (HL.B.B1<<1) | (HL.B.B1>>7); +AF.B.B0 |= ZeroTable[HL.B.B1]; +break; +case 0x05: +// RLC L +AF.B.B0 = (HL.B.B0 & 0x80)?C_FLAG:0; +HL.B.B0 = (HL.B.B0<<1) | (HL.B.B0>>7); +AF.B.B0 |= ZeroTable[HL.B.B0]; +break; +case 0x06: +// RLC (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0 = (tempValue & 0x80)?C_FLAG:0; +tempValue = (tempValue<<1) | (tempValue>>7); +AF.B.B0 |= ZeroTable[tempValue]; +gbWriteMemory(HL.W,tempValue); +break; +case 0x07: +// RLC A +AF.B.B0 = (AF.B.B1 & 0x80)?C_FLAG:0; +AF.B.B1 = (AF.B.B1<<1) | (AF.B.B1>>7); +AF.B.B0 |= ZeroTable[AF.B.B1]; +break; +case 0x08: +// RRC B +AF.B.B0=(BC.B.B1&0x01 ? C_FLAG : 0); +BC.B.B1=(BC.B.B1>>1)|(BC.B.B1<<7); +AF.B.B0|=ZeroTable[BC.B.B1]; +break; +case 0x09: +// RRC C +AF.B.B0=(BC.B.B0&0x01 ? C_FLAG : 0); +BC.B.B0=(BC.B.B0>>1)|(BC.B.B0<<7); +AF.B.B0|=ZeroTable[BC.B.B0]; +break; +case 0x0a: +// RRC D +AF.B.B0=(DE.B.B1&0x01 ? C_FLAG : 0); +DE.B.B1=(DE.B.B1>>1)|(DE.B.B1<<7); +AF.B.B0|=ZeroTable[DE.B.B1]; +break; +case 0x0b: +// RRC E +AF.B.B0=(DE.B.B0&0x01 ? C_FLAG : 0); +DE.B.B0=(DE.B.B0>>1)|(DE.B.B0<<7); +AF.B.B0|=ZeroTable[DE.B.B0]; +break; +case 0x0c: +// RRC H +AF.B.B0=(HL.B.B1&0x01 ? C_FLAG : 0); +HL.B.B1=(HL.B.B1>>1)|(HL.B.B1<<7); +AF.B.B0|=ZeroTable[HL.B.B1]; +break; +case 0x0d: +// RRC L +AF.B.B0=(HL.B.B0&0x01 ? C_FLAG : 0); +HL.B.B0=(HL.B.B0>>1)|(HL.B.B0<<7); +AF.B.B0|=ZeroTable[HL.B.B0]; +break; +case 0x0e: +// RRC (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(tempValue&0x01 ? C_FLAG : 0); +tempValue=(tempValue>>1)|(tempValue<<7); +AF.B.B0|=ZeroTable[tempValue]; +gbWriteMemory(HL.W,tempValue); +break; +case 0x0f: +// RRC A +AF.B.B0=(AF.B.B1&0x01 ? C_FLAG : 0); +AF.B.B1=(AF.B.B1>>1)|(AF.B.B1<<7); +AF.B.B0|=ZeroTable[AF.B.B1]; +break; +case 0x10: +// RL B +if(BC.B.B1&0x80) + { + BC.B.B1=(BC.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B1]|C_FLAG; + } +else + { + BC.B.B1=(BC.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B1]; + } +break; +case 0x11: +// RL C +if(BC.B.B0&0x80) + { + BC.B.B0=(BC.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B0]|C_FLAG; + } +else + { + BC.B.B0=(BC.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B0]; + } +break; +case 0x12: +// RL D +if(DE.B.B1&0x80) + { + DE.B.B1=(DE.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B1]|C_FLAG; + } +else + { + DE.B.B1=(DE.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B1]; + } +break; +case 0x13: +// RL E +if(DE.B.B0&0x80) + { + DE.B.B0=(DE.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B0]|C_FLAG; + } +else + { + DE.B.B0=(DE.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B0]; + } +break; +case 0x14: +// RL H +if(HL.B.B1&0x80) + { + HL.B.B1=(HL.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B1]|C_FLAG; + } +else + { + HL.B.B1=(HL.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B1]; + } +break; +case 0x15: +// RL L +if(HL.B.B0&0x80) + { + HL.B.B0=(HL.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B0]|C_FLAG; + } +else + { + HL.B.B0=(HL.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B0]; + } +break; +case 0x16: +// RL (HL) +tempValue=gbReadMemory(HL.W); +if(tempValue&0x80) + { + tempValue=(tempValue<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[tempValue]|C_FLAG; + } +else + { + tempValue=(tempValue<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[tempValue]; + } +gbWriteMemory(HL.W,tempValue); +break; +case 0x17: +// RL A +if(AF.B.B1&0x80) + { + AF.B.B1=(AF.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[AF.B.B1]|C_FLAG; + } +else + { + AF.B.B1=(AF.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[AF.B.B1]; + } +break; +case 0x18: +// RR B +if(BC.B.B1&0x01) + { + BC.B.B1=(BC.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B1]|C_FLAG; + } +else + { + BC.B.B1=(BC.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B1]; + } +break; +case 0x19: +// RR C +if(BC.B.B0&0x01) + { + BC.B.B0=(BC.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B0]|C_FLAG; + } +else + { + BC.B.B0=(BC.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B0]; + } +break; +case 0x1a: +// RR D +if(DE.B.B1&0x01) + { + DE.B.B1=(DE.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B1]|C_FLAG; + } +else + { + DE.B.B1=(DE.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B1]; + } +break; +case 0x1b: +// RR E +if(DE.B.B0&0x01) + { + DE.B.B0=(DE.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B0]|C_FLAG; + } +else + { + DE.B.B0=(DE.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B0]; + } +break; +case 0x1c: +// RR H +if(HL.B.B1&0x01) + { + HL.B.B1=(HL.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B1]|C_FLAG; + } +else + { + HL.B.B1=(HL.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B1]; + } +break; +case 0x1d: +// RR L +if(HL.B.B0&0x01) + { + HL.B.B0=(HL.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B0]|C_FLAG; + } +else + { + HL.B.B0=(HL.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B0]; + } +break; +case 0x1e: +// RR (HL) +tempValue=gbReadMemory(HL.W); +if(tempValue&0x01) + { + tempValue=(tempValue>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[tempValue]|C_FLAG; + } +else + { + tempValue=(tempValue>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[tempValue]; + } +gbWriteMemory(HL.W,tempValue); +break; +case 0x1f: +// RR A +if(AF.B.B1&0x01) + { + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[AF.B.B1]|C_FLAG; + } +else + { + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[AF.B.B1]; + } +break; +case 0x20: +// SLA B +AF.B.B0=(BC.B.B1&0x80?C_FLAG : 0); +BC.B.B1<<=1; +AF.B.B0|=ZeroTable[BC.B.B1]; +break; +case 0x21: +// SLA C +AF.B.B0=(BC.B.B0&0x80?C_FLAG : 0); +BC.B.B0<<=1; +AF.B.B0|=ZeroTable[BC.B.B0]; +break; +case 0x22: +// SLA D +AF.B.B0=(DE.B.B1&0x80?C_FLAG : 0); +DE.B.B1<<=1; +AF.B.B0|=ZeroTable[DE.B.B1]; +break; +case 0x23: +// SLA E +AF.B.B0=(DE.B.B0&0x80?C_FLAG : 0); +DE.B.B0<<=1; +AF.B.B0|=ZeroTable[DE.B.B0]; +break; +case 0x24: +// SLA H +AF.B.B0=(HL.B.B1&0x80?C_FLAG : 0); +HL.B.B1<<=1; +AF.B.B0|=ZeroTable[HL.B.B1]; +break; +case 0x25: +// SLA L +AF.B.B0=(HL.B.B0&0x80?C_FLAG : 0); +HL.B.B0<<=1; +AF.B.B0|=ZeroTable[HL.B.B0]; +break; +case 0x26: +// SLA (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(tempValue&0x80?C_FLAG : 0); +tempValue<<=1; +AF.B.B0|=ZeroTable[tempValue]; +gbWriteMemory(HL.W,tempValue); +break; +case 0x27: +// SLA A +AF.B.B0=(AF.B.B1&0x80?C_FLAG : 0); +AF.B.B1<<=1; +AF.B.B0|=ZeroTable[AF.B.B1]; +break; +case 0x28: +// SRA B +AF.B.B0=(BC.B.B1&0x01 ? C_FLAG: 0); +BC.B.B1=(BC.B.B1>>1)|(BC.B.B1&0x80); +AF.B.B0|=ZeroTable[BC.B.B1]; +break; +case 0x29: +// SRA C +AF.B.B0=(BC.B.B0&0x01 ? C_FLAG: 0); +BC.B.B0=(BC.B.B0>>1)|(BC.B.B0&0x80); +AF.B.B0|=ZeroTable[BC.B.B0]; +break; +case 0x2a: +// SRA D +AF.B.B0=(DE.B.B1&0x01 ? C_FLAG: 0); +DE.B.B1=(DE.B.B1>>1)|(DE.B.B1&0x80); +AF.B.B0|=ZeroTable[DE.B.B1]; +break; +case 0x2b: +// SRA E +AF.B.B0=(DE.B.B0&0x01 ? C_FLAG: 0); +DE.B.B0=(DE.B.B0>>1)|(DE.B.B0&0x80); +AF.B.B0|=ZeroTable[DE.B.B0]; +break; +case 0x2c: +// SRA H +AF.B.B0=(HL.B.B1&0x01 ? C_FLAG: 0); +HL.B.B1=(HL.B.B1>>1)|(HL.B.B1&0x80); +AF.B.B0|=ZeroTable[HL.B.B1]; +break; +case 0x2d: +// SRA L +AF.B.B0=(HL.B.B0&0x01 ? C_FLAG: 0); +HL.B.B0=(HL.B.B0>>1)|(HL.B.B0&0x80); +AF.B.B0|=ZeroTable[HL.B.B0]; +break; +case 0x2e: +// SRA (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(tempValue&0x01 ? C_FLAG: 0); +tempValue=(tempValue>>1)|(tempValue&0x80); +AF.B.B0|=ZeroTable[tempValue]; +gbWriteMemory(HL.W,tempValue); +break; +case 0x2f: +// SRA A +AF.B.B0=(AF.B.B1&0x01 ? C_FLAG: 0); +AF.B.B1=(AF.B.B1>>1)|(AF.B.B1&0x80); +AF.B.B0|=ZeroTable[AF.B.B1]; +break; +case 0x30: +// SWAP B +BC.B.B1 = (BC.B.B1&0xf0)>>4 | (BC.B.B1&0x0f)<<4; +AF.B.B0 = ZeroTable[BC.B.B1]; +break; +case 0x31: +// SWAP C +BC.B.B0 = (BC.B.B0&0xf0)>>4 | (BC.B.B0&0x0f)<<4; +AF.B.B0 = ZeroTable[BC.B.B0]; +break; +case 0x32: +// SWAP D +DE.B.B1 = (DE.B.B1&0xf0)>>4 | (DE.B.B1&0x0f)<<4; +AF.B.B0 = ZeroTable[DE.B.B1]; +break; +case 0x33: +// SWAP E +DE.B.B0 = (DE.B.B0&0xf0)>>4 | (DE.B.B0&0x0f)<<4; +AF.B.B0 = ZeroTable[DE.B.B0]; +break; +case 0x34: +// SWAP H +HL.B.B1 = (HL.B.B1&0xf0)>>4 | (HL.B.B1&0x0f)<<4; +AF.B.B0 = ZeroTable[HL.B.B1]; +break; +case 0x35: +// SWAP L +HL.B.B0 = (HL.B.B0&0xf0)>>4 | (HL.B.B0&0x0f)<<4; +AF.B.B0 = ZeroTable[HL.B.B0]; +break; +case 0x36: +// SWAP (HL) +tempValue=gbReadMemory(HL.W); +tempValue = (tempValue&0xf0)>>4 | (tempValue&0x0f)<<4; +AF.B.B0 = ZeroTable[tempValue]; +gbWriteMemory(HL.W,tempValue); +break; +case 0x37: +// SWAP A +AF.B.B1 = (AF.B.B1&0xf0)>>4 | (AF.B.B1&0x0f)<<4; +AF.B.B0 = ZeroTable[AF.B.B1]; +break; +case 0x38: +// SRL B +AF.B.B0=(BC.B.B1&0x01)?C_FLAG:0; +BC.B.B1>>=1; +AF.B.B0|=ZeroTable[BC.B.B1]; +break; +case 0x39: +// SRL C +AF.B.B0=(BC.B.B0&0x01)?C_FLAG:0; +BC.B.B0>>=1; +AF.B.B0|=ZeroTable[BC.B.B0]; +break; +case 0x3a: +// SRL D +AF.B.B0=(DE.B.B1&0x01)?C_FLAG:0; +DE.B.B1>>=1; +AF.B.B0|=ZeroTable[DE.B.B1]; +break; +case 0x3b: +// SRL E +AF.B.B0=(DE.B.B0&0x01)?C_FLAG:0; +DE.B.B0>>=1; +AF.B.B0|=ZeroTable[DE.B.B0]; +break; +case 0x3c: +// SRL H +AF.B.B0=(HL.B.B1&0x01)?C_FLAG:0; +HL.B.B1>>=1; +AF.B.B0|=ZeroTable[HL.B.B1]; +break; +case 0x3d: +// SRL L +AF.B.B0=(HL.B.B0&0x01)?C_FLAG:0; +HL.B.B0>>=1; +AF.B.B0|=ZeroTable[HL.B.B0]; +break; +case 0x3e: +// SRL (HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(tempValue&0x01)?C_FLAG:0; +tempValue>>=1; +AF.B.B0|=ZeroTable[tempValue]; +gbWriteMemory(HL.W,tempValue); +break; +case 0x3f: +// SRL A +AF.B.B0=(AF.B.B1&0x01)?C_FLAG:0; +AF.B.B1>>=1; +AF.B.B0|=ZeroTable[AF.B.B1]; +break; +case 0x40: +// BIT 0,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<0)? 0:Z_FLAG); +break; +case 0x41: +// BIT 0,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<0)? 0:Z_FLAG); +break; +case 0x42: +// BIT 0,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<0)? 0:Z_FLAG); +break; +case 0x43: +// BIT 0,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<0)? 0:Z_FLAG); +break; +case 0x44: +// BIT 0,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<0)? 0:Z_FLAG); +break; +case 0x45: +// BIT 0,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<0)? 0:Z_FLAG); +break; +case 0x46: +// BIT 0,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<0)? 0:Z_FLAG); +break; +case 0x47: +// BIT 0,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<0)? 0:Z_FLAG); +break; +case 0x48: +// BIT 1,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<1)? 0:Z_FLAG); +break; +case 0x49: +// BIT 1,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<1)? 0:Z_FLAG); +break; +case 0x4a: +// BIT 1,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<1)? 0:Z_FLAG); +break; +case 0x4b: +// BIT 1,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<1)? 0:Z_FLAG); +break; +case 0x4c: +// BIT 1,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<1)? 0:Z_FLAG); +break; +case 0x4d: +// BIT 1,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<1)? 0:Z_FLAG); +break; +case 0x4e: +// BIT 1,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<1)? 0:Z_FLAG); +break; +case 0x4f: +// BIT 1,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<1)? 0:Z_FLAG); +break; +case 0x50: +// BIT 2,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<2)? 0:Z_FLAG); +break; +case 0x51: +// BIT 2,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<2)? 0:Z_FLAG); +break; +case 0x52: +// BIT 2,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<2)? 0:Z_FLAG); +break; +case 0x53: +// BIT 2,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<2)? 0:Z_FLAG); +break; +case 0x54: +// BIT 2,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<2)? 0:Z_FLAG); +break; +case 0x55: +// BIT 2,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<2)? 0:Z_FLAG); +break; +case 0x56: +// BIT 2,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<2)? 0:Z_FLAG); +break; +case 0x57: +// BIT 2,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<2)? 0:Z_FLAG); +break; +case 0x58: +// BIT 3,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<3)? 0:Z_FLAG); +break; +case 0x59: +// BIT 3,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<3)? 0:Z_FLAG); +break; +case 0x5a: +// BIT 3,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<3)? 0:Z_FLAG); +break; +case 0x5b: +// BIT 3,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<3)? 0:Z_FLAG); +break; +case 0x5c: +// BIT 3,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<3)? 0:Z_FLAG); +break; +case 0x5d: +// BIT 3,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<3)? 0:Z_FLAG); +break; +case 0x5e: +// BIT 3,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<3)? 0:Z_FLAG); +break; +case 0x5f: +// BIT 3,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<3)? 0:Z_FLAG); +break; +case 0x60: +// BIT 4,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<4)? 0:Z_FLAG); +break; +case 0x61: +// BIT 4,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<4)? 0:Z_FLAG); +break; +case 0x62: +// BIT 4,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<4)? 0:Z_FLAG); +break; +case 0x63: +// BIT 4,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<4)? 0:Z_FLAG); +break; +case 0x64: +// BIT 4,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<4)? 0:Z_FLAG); +break; +case 0x65: +// BIT 4,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<4)? 0:Z_FLAG); +break; +case 0x66: +// BIT 4,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<4)? 0:Z_FLAG); +break; +case 0x67: +// BIT 4,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<4)? 0:Z_FLAG); +break; +case 0x68: +// BIT 5,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<5)? 0:Z_FLAG); +break; +case 0x69: +// BIT 5,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<5)? 0:Z_FLAG); +break; +case 0x6a: +// BIT 5,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<5)? 0:Z_FLAG); +break; +case 0x6b: +// BIT 5,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<5)? 0:Z_FLAG); +break; +case 0x6c: +// BIT 5,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<5)? 0:Z_FLAG); +break; +case 0x6d: +// BIT 5,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<5)? 0:Z_FLAG); +break; +case 0x6e: +// BIT 5,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<5)? 0:Z_FLAG); +break; +case 0x6f: +// BIT 5,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<5)? 0:Z_FLAG); +break; +case 0x70: +// BIT 6,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<6)? 0:Z_FLAG); +break; +case 0x71: +// BIT 6,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<6)? 0:Z_FLAG); +break; +case 0x72: +// BIT 6,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<6)? 0:Z_FLAG); +break; +case 0x73: +// BIT 6,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<6)? 0:Z_FLAG); +break; +case 0x74: +// BIT 6,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<6)? 0:Z_FLAG); +break; +case 0x75: +// BIT 6,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<6)? 0:Z_FLAG); +break; +case 0x76: +// BIT 6,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<6)? 0:Z_FLAG); +break; +case 0x77: +// BIT 6,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<6)? 0:Z_FLAG); +break; +case 0x78: +// BIT 7,B +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<7)? 0:Z_FLAG); +break; +case 0x79: +// BIT 7,C +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<7)? 0:Z_FLAG); +break; +case 0x7a: +// BIT 7,D +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<7)? 0:Z_FLAG); +break; +case 0x7b: +// BIT 7,E +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<7)? 0:Z_FLAG); +break; +case 0x7c: +// BIT 7,H +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<7)? 0:Z_FLAG); +break; +case 0x7d: +// BIT 7,L +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<7)? 0:Z_FLAG); +break; +case 0x7e: +// BIT 7,(HL) +tempValue=gbReadMemory(HL.W); +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<7)? 0:Z_FLAG); +break; +case 0x7f: +// BIT 7,A +AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<7)? 0:Z_FLAG); +break; +case 0x80: +// RES 0,B +BC.B.B1&=~(1<<0); +break; +case 0x81: +// RES 0,C +BC.B.B0&=~(1<<0); +break; +case 0x82: +// RES 0,D +DE.B.B1&=~(1<<0); +break; +case 0x83: +// RES 0,E +DE.B.B0&=~(1<<0); +break; +case 0x84: +// RES 0,H +HL.B.B1&=~(1<<0); +break; +case 0x85: +// RES 0,L +HL.B.B0&=~(1<<0); +break; +case 0x86: +// RES 0,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<0); +gbWriteMemory(HL.W,tempValue); +break; +case 0x87: +// RES 0,A +AF.B.B1&=~(1<<0); +break; +case 0x88: +// RES 1,B +BC.B.B1&=~(1<<1); +break; +case 0x89: +// RES 1,C +BC.B.B0&=~(1<<1); +break; +case 0x8a: +// RES 1,D +DE.B.B1&=~(1<<1); +break; +case 0x8b: +// RES 1,E +DE.B.B0&=~(1<<1); +break; +case 0x8c: +// RES 1,H +HL.B.B1&=~(1<<1); +break; +case 0x8d: +// RES 1,L +HL.B.B0&=~(1<<1); +break; +case 0x8e: +// RES 1,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<1); +gbWriteMemory(HL.W,tempValue); +break; +case 0x8f: +// RES 1,A +AF.B.B1&=~(1<<1); +break; +case 0x90: +// RES 2,B +BC.B.B1&=~(1<<2); +break; +case 0x91: +// RES 2,C +BC.B.B0&=~(1<<2); +break; +case 0x92: +// RES 2,D +DE.B.B1&=~(1<<2); +break; +case 0x93: +// RES 2,E +DE.B.B0&=~(1<<2); +break; +case 0x94: +// RES 2,H +HL.B.B1&=~(1<<2); +break; +case 0x95: +// RES 2,L +HL.B.B0&=~(1<<2); +break; +case 0x96: +// RES 2,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<2); +gbWriteMemory(HL.W,tempValue); +break; +case 0x97: +// RES 2,A +AF.B.B1&=~(1<<2); +break; +case 0x98: +// RES 3,B +BC.B.B1&=~(1<<3); +break; +case 0x99: +// RES 3,C +BC.B.B0&=~(1<<3); +break; +case 0x9a: +// RES 3,D +DE.B.B1&=~(1<<3); +break; +case 0x9b: +// RES 3,E +DE.B.B0&=~(1<<3); +break; +case 0x9c: +// RES 3,H +HL.B.B1&=~(1<<3); +break; +case 0x9d: +// RES 3,L +HL.B.B0&=~(1<<3); +break; +case 0x9e: +// RES 3,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<3); +gbWriteMemory(HL.W,tempValue); +break; +case 0x9f: +// RES 3,A +AF.B.B1&=~(1<<3); +break; +case 0xa0: +// RES 4,B +BC.B.B1&=~(1<<4); +break; +case 0xa1: +// RES 4,C +BC.B.B0&=~(1<<4); +break; +case 0xa2: +// RES 4,D +DE.B.B1&=~(1<<4); +break; +case 0xa3: +// RES 4,E +DE.B.B0&=~(1<<4); +break; +case 0xa4: +// RES 4,H +HL.B.B1&=~(1<<4); +break; +case 0xa5: +// RES 4,L +HL.B.B0&=~(1<<4); +break; +case 0xa6: +// RES 4,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<4); +gbWriteMemory(HL.W,tempValue); +break; +case 0xa7: +// RES 4,A +AF.B.B1&=~(1<<4); +break; +case 0xa8: +// RES 5,B +BC.B.B1&=~(1<<5); +break; +case 0xa9: +// RES 5,C +BC.B.B0&=~(1<<5); +break; +case 0xaa: +// RES 5,D +DE.B.B1&=~(1<<5); +break; +case 0xab: +// RES 5,E +DE.B.B0&=~(1<<5); +break; +case 0xac: +// RES 5,H +HL.B.B1&=~(1<<5); +break; +case 0xad: +// RES 5,L +HL.B.B0&=~(1<<5); +break; +case 0xae: +// RES 5,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<5); +gbWriteMemory(HL.W,tempValue); +break; +case 0xaf: +// RES 5,A +AF.B.B1&=~(1<<5); +break; +case 0xb0: +// RES 6,B +BC.B.B1&=~(1<<6); +break; +case 0xb1: +// RES 6,C +BC.B.B0&=~(1<<6); +break; +case 0xb2: +// RES 6,D +DE.B.B1&=~(1<<6); +break; +case 0xb3: +// RES 6,E +DE.B.B0&=~(1<<6); +break; +case 0xb4: +// RES 6,H +HL.B.B1&=~(1<<6); +break; +case 0xb5: +// RES 6,L +HL.B.B0&=~(1<<6); +break; +case 0xb6: +// RES 6,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<6); +gbWriteMemory(HL.W,tempValue); +break; +case 0xb7: +// RES 6,A +AF.B.B1&=~(1<<6); +break; +case 0xb8: +// RES 7,B +BC.B.B1&=~(1<<7); +break; +case 0xb9: +// RES 7,C +BC.B.B0&=~(1<<7); +break; +case 0xba: +// RES 7,D +DE.B.B1&=~(1<<7); +break; +case 0xbb: +// RES 7,E +DE.B.B0&=~(1<<7); +break; +case 0xbc: +// RES 7,H +HL.B.B1&=~(1<<7); +break; +case 0xbd: +// RES 7,L +HL.B.B0&=~(1<<7); +break; +case 0xbe: +// RES 7,(HL) +tempValue=gbReadMemory(HL.W); +tempValue&=~(1<<7); +gbWriteMemory(HL.W,tempValue); +break; +case 0xbf: +// RES 7,A +AF.B.B1&=~(1<<7); +break; +case 0xc0: +// SET 0,B +BC.B.B1|=1<<0; +break; +case 0xc1: +// SET 0,C +BC.B.B0|=1<<0; +break; +case 0xc2: +// SET 0,D +DE.B.B1|=1<<0; +break; +case 0xc3: +// SET 0,E +DE.B.B0|=1<<0; +break; +case 0xc4: +// SET 0,H +HL.B.B1|=1<<0; +break; +case 0xc5: +// SET 0,L +HL.B.B0|=1<<0; +break; +case 0xc6: +// SET 0,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<0; +gbWriteMemory(HL.W,tempValue); +break; +case 0xc7: +// SET 0,A +AF.B.B1|=1<<0; +break; +case 0xc8: +// SET 1,B +BC.B.B1|=1<<1; +break; +case 0xc9: +// SET 1,C +BC.B.B0|=1<<1; +break; +case 0xca: +// SET 1,D +DE.B.B1|=1<<1; +break; +case 0xcb: +// SET 1,E +DE.B.B0|=1<<1; +break; +case 0xcc: +// SET 1,H +HL.B.B1|=1<<1; +break; +case 0xcd: +// SET 1,L +HL.B.B0|=1<<1; +break; +case 0xce: +// SET 1,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<1; +gbWriteMemory(HL.W,tempValue); +break; +case 0xcf: +// SET 1,A +AF.B.B1|=1<<1; +break; +case 0xd0: +// SET 2,B +BC.B.B1|=1<<2; +break; +case 0xd1: +// SET 2,C +BC.B.B0|=1<<2; +break; +case 0xd2: +// SET 2,D +DE.B.B1|=1<<2; +break; +case 0xd3: +// SET 2,E +DE.B.B0|=1<<2; +break; +case 0xd4: +// SET 2,H +HL.B.B1|=1<<2; +break; +case 0xd5: +// SET 2,L +HL.B.B0|=1<<2; +break; +case 0xd6: +// SET 2,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<2; +gbWriteMemory(HL.W,tempValue); +break; +case 0xd7: +// SET 2,A +AF.B.B1|=1<<2; +break; +case 0xd8: +// SET 3,B +BC.B.B1|=1<<3; +break; +case 0xd9: +// SET 3,C +BC.B.B0|=1<<3; +break; +case 0xda: +// SET 3,D +DE.B.B1|=1<<3; +break; +case 0xdb: +// SET 3,E +DE.B.B0|=1<<3; +break; +case 0xdc: +// SET 3,H +HL.B.B1|=1<<3; +break; +case 0xdd: +// SET 3,L +HL.B.B0|=1<<3; +break; +case 0xde: +// SET 3,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<3; +gbWriteMemory(HL.W,tempValue); +break; +case 0xdf: +// SET 3,A +AF.B.B1|=1<<3; +break; +case 0xe0: +// SET 4,B +BC.B.B1|=1<<4; +break; +case 0xe1: +// SET 4,C +BC.B.B0|=1<<4; +break; +case 0xe2: +// SET 4,D +DE.B.B1|=1<<4; +break; +case 0xe3: +// SET 4,E +DE.B.B0|=1<<4; +break; +case 0xe4: +// SET 4,H +HL.B.B1|=1<<4; +break; +case 0xe5: +// SET 4,L +HL.B.B0|=1<<4; +break; +case 0xe6: +// SET 4,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<4; +gbWriteMemory(HL.W,tempValue); +break; +case 0xe7: +// SET 4,A +AF.B.B1|=1<<4; +break; +case 0xe8: +// SET 5,B +BC.B.B1|=1<<5; +break; +case 0xe9: +// SET 5,C +BC.B.B0|=1<<5; +break; +case 0xea: +// SET 5,D +DE.B.B1|=1<<5; +break; +case 0xeb: +// SET 5,E +DE.B.B0|=1<<5; +break; +case 0xec: +// SET 5,H +HL.B.B1|=1<<5; +break; +case 0xed: +// SET 5,L +HL.B.B0|=1<<5; +break; +case 0xee: +// SET 5,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<5; +gbWriteMemory(HL.W,tempValue); +break; +case 0xef: +// SET 5,A +AF.B.B1|=1<<5; +break; +case 0xf0: +// SET 6,B +BC.B.B1|=1<<6; +break; +case 0xf1: +// SET 6,C +BC.B.B0|=1<<6; +break; +case 0xf2: +// SET 6,D +DE.B.B1|=1<<6; +break; +case 0xf3: +// SET 6,E +DE.B.B0|=1<<6; +break; +case 0xf4: +// SET 6,H +HL.B.B1|=1<<6; +break; +case 0xf5: +// SET 6,L +HL.B.B0|=1<<6; +break; +case 0xf6: +// SET 6,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<6; +gbWriteMemory(HL.W,tempValue); +break; +case 0xf7: +// SET 6,A +AF.B.B1|=1<<6; +break; +case 0xf8: +// SET 7,B +BC.B.B1|=1<<7; +break; +case 0xf9: +// SET 7,C +BC.B.B0|=1<<7; +break; +case 0xfa: +// SET 7,D +DE.B.B1|=1<<7; +break; +case 0xfb: +// SET 7,E +DE.B.B0|=1<<7; +break; +case 0xfc: +// SET 7,H +HL.B.B1|=1<<7; +break; +case 0xfd: +// SET 7,L +HL.B.B0|=1<<7; +break; +case 0xfe: +// SET 7,(HL) +tempValue=gbReadMemory(HL.W); +tempValue|=1<<7; +gbWriteMemory(HL.W,tempValue); +break; +case 0xff: +// SET 7,A +AF.B.B1|=1<<7; +break; +default: +systemMessage(0, N_("Unknown opcode %02x at %04x"), + gbReadOpcode(PC.W-1),PC.W-1); +emulating = false; +return; diff --git a/src/gb/gbDis.cpp b/src/gb/gbDis.cpp new file mode 100644 index 0000000..39f5ac9 --- /dev/null +++ b/src/gb/gbDis.cpp @@ -0,0 +1,260 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "../System.h" +#include "gbGlobals.h" + +typedef struct + { + u8 mask; + u8 value; + char *mnen; + } +GBOPCODE; + +#define GB_READ(x) gbMemoryMap[(x)>>12][(x)&0xfff] + +static char *registers[] = + { "B", "C", "D", "E", "H", "L", "(HL)", "A" }; + +static char *registers16[] = + { "BC", "DE", "HL", "SP", // for some operations + "BC", "DE", "HL", "AF" }; // for push/pop + +static char *cond[] = + { "NZ", "Z", "NC", "C" }; + +static char hexDigits[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + +static GBOPCODE opcodes[] = { + { 0xff, 0x00, "NOP" }, + { 0xcf, 0x01, "LD %R4,%W" }, + { 0xff, 0x02, "LD (BC),A" }, + { 0xcf, 0x03, "INC %R4" }, + { 0xc7, 0x04, "INC %r3" }, + { 0xc7, 0x05, "DEC %r3" }, + { 0xc7, 0x06, "LD %r3,%B" }, + { 0xff, 0x07, "RLCA" }, + { 0xff, 0x08, "LD (%W),SP" }, + { 0xcf, 0x09, "ADD HL,%R4" }, + { 0xff, 0x0a, "LD A,(BC)" }, + { 0xcf, 0x0b, "DEC %R4" }, + { 0xff, 0x0f, "RRCA" }, + { 0xff, 0x10, "STOP" }, + { 0xff, 0x12, "LD (DE),A" }, + { 0xff, 0x17, "RLA" }, + { 0xff, 0x18, "JR %d" }, + { 0xff, 0x1a, "LD A,(DE)" }, + { 0xff, 0x1f, "RRA" }, + { 0xe7, 0x20, "JR %c3,%d" }, + { 0xff, 0x22, "LDI (HL),A" }, + { 0xff, 0x27, "DAA" }, + { 0xff, 0x2a, "LDI A,(HL)" }, + { 0xff, 0x2f, "CPL" }, + { 0xff, 0x32, "LDD (HL),A" }, + { 0xff, 0x37, "SCF" }, + { 0xff, 0x3a, "LDD A,(HL)" }, + { 0xff, 0x3f, "CCF" }, + { 0xff, 0x76, "HALT" }, + { 0xc0, 0x40, "LD %r3,%r0" }, + { 0xf8, 0x80, "ADD A,%r0" }, + { 0xf8, 0x88, "ADC A,%r0" }, + { 0xf8, 0x90, "SUB %r0" }, + { 0xf8, 0x98, "SBC A,%r0" }, + { 0xf8, 0xa0, "AND %r0" }, + { 0xf8, 0xa8, "XOR %r0" }, + { 0xf8, 0xb0, "OR %r0" }, + { 0xf8, 0xb8, "CP %r0" }, + { 0xe7, 0xc0, "RET %c3" }, + { 0xcf, 0xc1, "POP %t4" }, + { 0xe7, 0xc2, "JP %c3,%W" }, + { 0xff, 0xc3, "JP %W" }, + { 0xe7, 0xc4, "CALL %c3,%W" }, + { 0xcf, 0xc5, "PUSH %t4" }, + { 0xff, 0xc6, "ADD A,%B" }, + { 0xc7, 0xc7, "RST %P" }, + { 0xff, 0xc9, "RET" }, + { 0xff, 0xcd, "CALL %W" }, + { 0xff, 0xce, "ADC %B" }, + { 0xff, 0xd6, "SUB %B" }, + { 0xff, 0xd9, "RETI" }, + { 0xff, 0xde, "SBC %B" }, + { 0xff, 0xe0, "LD (FF%B),A" }, + { 0xff, 0xe2, "LD (FF00h+C),A" }, + { 0xff, 0xe6, "AND %B" }, + { 0xff, 0xe8, "ADD SP,%D" }, + { 0xff, 0xe9, "LD PC,HL" }, + { 0xff, 0xea, "LD (%W),A" }, + { 0xff, 0xee, "XOR %B" }, + { 0xff, 0xf0, "LD A,(FF%B)" }, + { 0xff, 0xf2, "LD A,(FF00h+C)" }, + { 0xff, 0xf3, "DI" }, + { 0xff, 0xf6, "OR %B" }, + { 0xff, 0xf8, "LD HL,SP%D" }, + { 0xff, 0xf9, "LD SP,HL" }, + { 0xff, 0xfa, "LD A,(%W)" }, + { 0xff, 0xfb, "EI" }, + { 0xff, 0xfe, "CP %B" }, + { 0x00, 0x00, "DB %B" } + }; + +static GBOPCODE cbOpcodes[] = { + { 0xf8, 0x00, "RLC %r0" }, + { 0xf8, 0x08, "RRC %r0" }, + { 0xf8, 0x10, "RL %r0" }, + { 0xf8, 0x18, "RR %r0" }, + { 0xf8, 0x20, "SLA %r0" }, + { 0xf8, 0x28, "SRA %r0" }, + { 0xf8, 0x30, "SWAP %r0" }, + { 0xf8, 0x38, "SRL %r0" }, + { 0xc0, 0x40, "BIT %b,%r0" }, + { 0xc0, 0x80, "RES %b,%r0" }, + { 0xc0, 0xc0, "SET %b,%r0" }, + { 0x00, 0x00, "DB CBh,%B" } + }; + +static char *addHex(char *p, u8 value) +{ + *p++ = hexDigits[value >> 4]; + *p++ = hexDigits[value & 15]; + return p; +} + +static char *addHex16(char *p, u16 value) +{ + p = addHex(p, value>>8); + return addHex(p, value & 255); +} + +static char *addStr(char *p, char *s) +{ + while(*s) + { + *p++ = *s++; + } + return p; +} + +int gbDis(char *buffer, u16 address) +{ + char *p = buffer; + int instr = 1; + u16 addr = address; + sprintf(p, "%04x ", address); + p += 12; + + u8 opcode = GB_READ(address); + address++; + char *mnen; + GBOPCODE *op; + if(opcode == 0xcb) + { + opcode = GB_READ(address); + address++; + instr++; + op = cbOpcodes; + } + else + { + op = opcodes; + } + while(op->value != (opcode & op->mask)) op++; + mnen = op->mnen; + + u8 b0, b1; + s8 disp; + int shift; + + while(*mnen) + { + if(*mnen == '%') + { + mnen++; + switch(*mnen++) + { + case 'W': + b0 = GB_READ(address); + address++; + b1 = GB_READ(address); + address++; + p = addHex16(p, b0|b1<<8); + instr += 2; + *p++ = 'h'; + break; + case 'B': + p = addHex(p, GB_READ(address)); + *p++ = 'h'; + address++; + instr++; + break; + case 'D': + disp = GB_READ(address); + if(disp >= 0) + *p++ = '+'; + p += sprintf(p, "%d", disp); + instr++; + break; + case 'd': + disp = GB_READ(address); + address++; + p = addHex16(p, address+disp); + *p++ = 'h'; + instr++; + break; + case 'b': + // kind of a hack, but it works :-) + *p++ = hexDigits[(opcode >> 3) & 7]; + break; + case 'r': + shift = *mnen++ - '0'; + p = addStr(p, registers[(opcode >> shift) & 7]); + break; + case 'R': + shift = *mnen++ - '0'; + p = addStr(p, registers16[(opcode >> shift) & 3]); + break; + case 't': + shift = *mnen++ - '0'; + p = addStr(p, registers16[4+((opcode >> shift) & 3)]); + break; + case 'P': + p = addHex(p, ((opcode >> 3) & 7) * 8); + break; + case 'c': + shift = *mnen++ - '0'; + p = addStr(p, cond[(opcode >> shift) & 3]); + break; + } + } + else + *p++ = *mnen++; + } + for(int i = 0; i < instr; i++) + { + u16 a = addr + i; + addHex(buffer+5+i*2, GB_READ(a)); + } + *p = 0; + return instr; +} diff --git a/src/gb/gbGfx.cpp b/src/gb/gbGfx.cpp new file mode 100644 index 0000000..ba863af --- /dev/null +++ b/src/gb/gbGfx.cpp @@ -0,0 +1,563 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "../GBA.h" +#include "gbGlobals.h" +#include "gbSGB.h" + +u8 gbInvertTab[256] = { + 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, + 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, + 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, + 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, + 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, + 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, + 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, + 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, + 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, + 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, + 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, + 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, + 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, + 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, + 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, + 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, + 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, + 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, + 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, + 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, + 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, + 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, + 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, + 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, + 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, + 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, + 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, + 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, + 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, + 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, + 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff + }; + +u16 gbLineMix[160]; + +void gbRenderLine() +{ + u8 * bank0; + u8 * bank1; + if(gbCgbMode) + { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } + else + { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int tile_map = 0x1800; + if((register_LCDC & 8) != 0) + tile_map = 0x1c00; + + int tile_pattern = 0x0800; + + if((register_LCDC & 16) != 0) + tile_pattern = 0x0000; + + int x = 0; + int y = register_LY; + + if(y >= 144) + return; + + // int yLine = (y + gbBorderRowSkip) * gbBorderLineSkip; + + int sx = register_SCX; + int sy = register_SCY; + + sy+=y; + + sy &= 255; + + int tx = sx >> 3; + int ty = sy >> 3; + + int bx = 1 << (7 - (sx & 7)); + int by = sy & 7; + + int tile_map_line_y = tile_map + ty * 32; + + int tile_map_address = tile_map_line_y + tx; + + u8 attrs = 0; + if(bank1 != NULL) + attrs = bank1[tile_map_address]; + + u8 tile = bank0[tile_map_address]; + + tile_map_address++; + + if((register_LCDC & 16) == 0) + { + if(tile < 128) tile += 128; + else tile -= 128; + } + + int tile_pattern_address = tile_pattern + tile * 16 + by*2; + + if(register_LCDC & 0x80) + { + if((register_LCDC & 0x01 || gbCgbMode) && (layerSettings & 0x0100)) + { + while(x < 160) + { + u8 tile_a = 0; + u8 tile_b = 0; + + if(attrs & 0x40) + { + tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2; + } + + if(attrs & 0x08) + { + tile_a = bank1[tile_pattern_address++]; + tile_b = bank1[tile_pattern_address]; + } + else + { + tile_a = bank0[tile_pattern_address++]; + tile_b = bank0[tile_pattern_address]; + } + + if(attrs & 0x20) + { + tile_a = gbInvertTab[tile_a]; + tile_b = gbInvertTab[tile_b]; + } + + while(bx > 0) + { + u8 c = (tile_a & bx) ? 1 : 0; + c += ((tile_b & bx) ? 2 : 0); + + gbLineBuffer[x] = c; // mark the gbLineBuffer color + + if(attrs & 0x80) + gbLineBuffer[x] |= 0x300; + + if(gbCgbMode) + { + c = c + (attrs & 7)*4; + } + else + { + c = gbBgp[c]; + if(gbSgbMode && !gbCgbMode) + { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c]] : + gbPalette[c]; + x++; + if(x >= 160) + break; + bx >>= 1; + } + tx++; + if(tx == 32) + tx = 0; + bx = 128; + + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + tile = bank0[tile_map_line_y + tx]; + + if((register_LCDC & 16) == 0) + { + if(tile < 128) tile += 128; + else tile -= 128; + } + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + } + else + { + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = gbPalette[0]; + gbLineBuffer[i] = 0; + } + } + + // do the window display + if((register_LCDC & 0x20) && (layerSettings & 0x2000)) + { + int wy = register_WY; + + if(y >= wy) + { + int wx = register_WX; + wx -= 7; + + if( wx <= 159 && gbWindowLine <= 143) + { + + tile_map = 0x1800; + + if((register_LCDC & 0x40) != 0) + tile_map = 0x1c00; + + if(gbWindowLine == -1) + { + gbWindowLine = 0; + } + + tx = 0; + ty = gbWindowLine >> 3; + + bx = 128; + by = gbWindowLine & 7; + + if(wx < 0) + { + bx >>= (-wx); + wx = 0; + } + + tile_map_line_y = tile_map + ty * 32; + + tile_map_address = tile_map_line_y + tx; + + x = wx; + + tile = bank0[tile_map_address]; + u8 attrs = 0; + if(bank1) + attrs = bank1[tile_map_address]; + tile_map_address++; + + if((register_LCDC & 16) == 0) + { + if(tile < 128) tile += 128; + else tile -= 128; + } + + tile_pattern_address = tile_pattern + tile * 16 + by*2; + + while(x < 160) + { + u8 tile_a = 0; + u8 tile_b = 0; + + if(attrs & 0x40) + { + tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2; + } + + if(attrs & 0x08) + { + tile_a = bank1[tile_pattern_address++]; + tile_b = bank1[tile_pattern_address]; + } + else + { + tile_a = bank0[tile_pattern_address++]; + tile_b = bank0[tile_pattern_address]; + } + + if(attrs & 0x20) + { + tile_a = gbInvertTab[tile_a]; + tile_b = gbInvertTab[tile_b]; + } + + while(bx > 0) + { + u8 c = (tile_a & bx) != 0 ? 1 : 0; + c += ((tile_b & bx) != 0 ? 2 : 0); + + if(attrs & 0x80) + gbLineBuffer[x] = 0x300 + c; + else + gbLineBuffer[x] = 0x100 + c; + + if(gbCgbMode) + { + c = c + (attrs & 7) * 4; + } + else + { + c = gbBgp[c]; + if(gbSgbMode && ! gbCgbMode) + { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c]] : + gbPalette[c]; + x++; + if(x >= 160) + break; + bx >>= 1; + } + tx++; + if(tx == 32) + tx = 0; + bx = 128; + tile = bank0[tile_map_line_y + tx]; + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + if((register_LCDC & 16) == 0) + { + if(tile < 128) tile += 128; + else tile -= 128; + } + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + gbWindowLine++; + } + } + } + } + else + { + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = gbPalette[0]; + gbLineBuffer[i] = 0; + } + } +} + +void gbDrawSpriteTile(int tile, int x,int y,int t, int flags, + int size,int spriteNumber) +{ + u8 * bank0; + u8 * bank1; + if(gbCgbMode) + { + if(register_VBK & 1) + { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } + else + { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } + } + else + { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int init = 0x0000; + + // int yLine = (y+gbBorderRowSkip) * gbBorderLineSkip; + + u8 *pal = gbObp0; + + int flipx = (flags & 0x20); + int flipy = (flags & 0x40); + + if((flags & 0x10)) + pal = gbObp1; + + if(flipy) + { + t = (size ? 15 : 7) - t; + } + + int prio = flags & 0x80; + + int address = init + tile * 16 + 2*t; + int a = 0; + int b = 0; + + if(gbCgbMode && flags & 0x08) + { + a = bank1[address++]; + b = bank1[address++]; + } + else + { + a = bank0[address++]; + b = bank0[address++]; + } + + for(int xx = 0; xx < 8; xx++) + { + u8 mask = 1 << (7-xx); + u8 c = 0; + if( (a & mask)) + c++; + if( (b & mask)) + c+=2; + + if(c==0) continue; + + int xxx = xx+x; + if(flipx) + xxx = (7-xx+x); + + if(xxx < 0 || xxx > 159) + continue; + + u16 color = gbLineBuffer[xxx]; + + if(prio) + { + if(color < 0x200 && ((color & 0xFF) != 0)) + continue; + } + if(color >= 0x300 && color != 0x300) + continue; + else if(color >= 0x200 && color < 0x300) + { + int sprite = color & 0xff; + + int spriteX = gbMemory[0xfe00 + 4 * sprite + 1] - 8; + + if(spriteX == x) + { + if(sprite < spriteNumber) + continue; + } + else + { + if(gbCgbMode) + { + if(sprite < spriteNumber) + continue; + } + else + { + if(spriteX < x+8) + continue; + } + } + } + + + gbLineBuffer[xxx] = 0x200 + spriteNumber; + + // make sure that sprites will work even in CGB mode + if(gbCgbMode) + { + c = c + (flags & 0x07)*4 + 32; + } + else + { + c = pal[c]; + + if(gbSgbMode && !gbCgbMode) + { + int dx = xxx >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + else + { + c += 4; + } + } + + gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c]] : + gbPalette[c]; + } +} + +void gbDrawSprites() +{ + int x = 0; + int y = 0; + int count = 0; + + int size = (register_LCDC & 4); + + if(!(register_LCDC & 0x80)) + return; + + if((register_LCDC & 2) && (layerSettings & 0x1000)) + { + int yc = register_LY; + + int address = 0xfe00; + for(int i = 0; i < 40; i++) + { + y = gbMemory[address++]; + x = gbMemory[address++]; + int tile = gbMemory[address++]; + if(size) + tile &= 254; + int flags = gbMemory[address++]; + + if(x > 0 && y > 0 && x < 168 && y < 160) + { + // check if sprite intersects current line + int t = yc -y + 16; + if(size && t >=0 && t < 16) + { + gbDrawSpriteTile(tile,x-8,yc,t,flags,size,i); + count++; + } + else if(!size && t >= 0 && t < 8) + { + gbDrawSpriteTile(tile, x-8, yc, t, flags,size,i); + count++; + } + } + // sprite limit reached! + if(count >= 10) + break; + } + } +} + diff --git a/src/gb/gbGlobals.cpp b/src/gb/gbGlobals.cpp new file mode 100644 index 0000000..90b1af1 --- /dev/null +++ b/src/gb/gbGlobals.cpp @@ -0,0 +1,54 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "../GBA.h" + +u8 *gbMemoryMap[16]; + +int gbRomSizeMask = 0; +int gbRomSize = 0; +int gbRamSizeMask = 0; +int gbRamSize = 0; + +u8 *gbMemory = NULL; +u8 *gbVram = NULL; +u8 *gbRom = NULL; +u8 *gbRam = NULL; +u8 *gbWram = NULL; +u16 *gbLineBuffer = NULL; + +u16 gbPalette[128]; +u8 gbBgp[4] = { 0, 1, 2, 3}; +u8 gbObp0[4] = { 0, 1, 2, 3}; +u8 gbObp1[4] = { 0, 1, 2, 3}; +int gbWindowLine = -1; + +int gbCgbMode = 0; + +u16 gbColorFilter[32768]; +int gbColorOption = 0; +int gbPaletteOption = 0; +int gbEmulatorType = 0; +int gbBorderOn = 1; +int gbBorderAutomatic = 0; +int gbBorderLineSkip = 160; +int gbBorderRowSkip = 0; +int gbBorderColumnSkip = 0; +int gbDmaTicks = 0; + +u8 (*gbSerialFunction)(u8) = NULL; diff --git a/src/gb/gbGlobals.h b/src/gb/gbGlobals.h new file mode 100644 index 0000000..380f933 --- /dev/null +++ b/src/gb/gbGlobals.h @@ -0,0 +1,68 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +extern int gbRomSizeMask; +extern int gbRomSize; +extern int gbRamSize; +extern int gbRamSizeMask; + +extern u8 *gbRom; +extern u8 *gbRam; +extern u8 *gbVram; +extern u8 *gbWram; +extern u8 *gbMemory; +extern u16 *gbLineBuffer; + +extern u8 *gbMemoryMap[16]; + +extern int gbFrameSkip; +extern u16 gbColorFilter[32768]; +extern int gbColorOption; +extern int gbPaletteOption; +extern int gbEmulatorType; +extern int gbBorderOn; +extern int gbBorderAutomatic; +extern int gbCgbMode; +extern int gbSgbMode; +extern int gbWindowLine; +extern int gbSpeed; +extern u8 gbBgp[4]; +extern u8 gbObp0[4]; +extern u8 gbObp1[4]; +extern u16 gbPalette[128]; + +extern u8 register_LCDC; +extern u8 register_LY; +extern u8 register_SCY; +extern u8 register_SCX; +extern u8 register_WY; +extern u8 register_WX; +extern u8 register_VBK; + +extern int emulating; + +extern int gbBorderLineSkip; +extern int gbBorderRowSkip; +extern int gbBorderColumnSkip; +extern int gbDmaTicks; + +extern void gbRenderLine(); +extern void gbDrawSprites(); + +extern u8 (*gbSerialFunction)(u8); diff --git a/src/gb/gbMemory.cpp b/src/gb/gbMemory.cpp new file mode 100644 index 0000000..5d23b55 --- /dev/null +++ b/src/gb/gbMemory.cpp @@ -0,0 +1,1074 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "../GBA.h" +#include "../Port.h" +#include "gbGlobals.h" +#include "gbMemory.h" + +mapperMBC1 gbDataMBC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0 // RAM address + }; + +// MBC1 ROM write registers +void mapperMBC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) + { + case 0x0000: // RAM enable register + gbDataMBC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + // value = value & 0x1f; + if(value == 0) + value = 1; + if(value == gbDataMBC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + // check current model + if(gbDataMBC1.mapperMemoryModel == 0) + { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbDataMBC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataMBC1.mapperMemoryModel == 1) + { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataMBC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMBC1.mapperRAMBank = value; + gbDataMBC1.mapperRAMAddress = tmpAddress; + } + else + { + // 16/8, set the high address + gbDataMBC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataMBC1.mapperROMBank << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataMBC1.mapperMemoryModel = value & 1; + break; + } +} + +// MBC1 RAM write +void mapperMBC1RAM(u16 address, u8 value) +{ + if(gbDataMBC1.mapperRAMEnable) + { + if(gbRamSize) + { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMBC1() +{ + int tmpAddress = gbDataMBC1.mapperROMBank << 14; + + // check current model + if(gbDataMBC1.mapperMemoryModel == 1) + { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) + { + gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; + } +} + +mapperMBC2 gbDataMBC2 = { + 0, // RAM enable + 1 // ROM bank + }; + +// MBC2 ROM write registers +void mapperMBC2ROM(u16 address, u8 value) +{ + switch(address & 0x6000) + { + case 0x0000: // RAM enable + if(!(address & 0x0100)) + { + gbDataMBC2.mapperRAMEnable = (value & 0x0f) == 0x0a; + } + break; + case 0x2000: // ROM bank select + if(address & 0x0100) + { + value &= 0x0f; + + if(value == 0) + value = 1; + if(gbDataMBC2.mapperROMBank != value) + { + gbDataMBC2.mapperROMBank = value; + + int tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + } + break; + } +} + +// MBC2 RAM write +void mapperMBC2RAM(u16 address, u8 value) +{ + if(gbDataMBC2.mapperRAMEnable) + { + if(gbRamSize && address < 0xa200) + { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMBC2() +{ + int tmpAddress = gbDataMBC2.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperMBC3 gbDataMBC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // timer clock latch + 0, // timer clock register + 0, // timer seconds + 0, // timer minutes + 0, // timer hours + 0, // timer days + 0, // timer control + 0, // timer latched seconds + 0, // timer latched minutes + 0, // timer latched hours + 0, // timer latched days + 0, // timer latched control + (time_t)-1 // last time + }; + +void memoryUpdateMBC3Clock() +{ + time_t now = time(NULL); + time_t diff = now - gbDataMBC3.mapperLastTime; + if(diff > 0) + { + // update the clock according to the last update time + gbDataMBC3.mapperSeconds += diff % 60; + if(gbDataMBC3.mapperSeconds > 59) + { + gbDataMBC3.mapperSeconds -= 60; + gbDataMBC3.mapperMinutes++; + } + + diff /= 60; + + gbDataMBC3.mapperMinutes += diff % 60; + if(gbDataMBC3.mapperMinutes > 60) + { + gbDataMBC3.mapperMinutes -= 60; + gbDataMBC3.mapperHours++; + } + + diff /= 60; + + gbDataMBC3.mapperHours += diff % 24; + if(gbDataMBC3.mapperHours > 24) + { + gbDataMBC3.mapperHours -= 24; + gbDataMBC3.mapperDays++; + } + diff /= 24; + + gbDataMBC3.mapperDays += diff; + if(gbDataMBC3.mapperDays > 255) + { + if(gbDataMBC3.mapperDays > 511) + { + gbDataMBC3.mapperDays %= 512; + gbDataMBC3.mapperControl |= 0x80; + } + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (gbDataMBC3.mapperDays>255 ? 1 : 0); + } + } + gbDataMBC3.mapperLastTime = now; +} + +// MBC3 ROM write registers +void mapperMBC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) + { + case 0x0000: // RAM enable register + gbDataMBC3.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataMBC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataMBC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + break; + case 0x4000: // RAM bank select + if(value < 8) + { + if(value == gbDataMBC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMBC3.mapperRAMBank = value; + gbDataMBC3.mapperRAMAddress = tmpAddress; + } + else + { + if(gbDataMBC3.mapperRAMEnable) + { + gbDataMBC3.mapperRAMBank = -1; + + gbDataMBC3.mapperClockRegister = value; + } + } + break; + case 0x6000: // clock latch + if(gbDataMBC3.mapperClockLatch == 0 && value == 1) + { + memoryUpdateMBC3Clock(); + gbDataMBC3.mapperLSeconds = gbDataMBC3.mapperSeconds; + gbDataMBC3.mapperLMinutes = gbDataMBC3.mapperMinutes; + gbDataMBC3.mapperLHours = gbDataMBC3.mapperHours; + gbDataMBC3.mapperLDays = gbDataMBC3.mapperDays; + gbDataMBC3.mapperLControl = gbDataMBC3.mapperControl; + } + if(value == 0x00 || value == 0x01) + gbDataMBC3.mapperClockLatch = value; + break; + } +} + +// MBC3 RAM write +void mapperMBC3RAM(u16 address, u8 value) +{ + if(gbDataMBC3.mapperRAMEnable) + { + if(gbDataMBC3.mapperRAMBank != -1) + { + if(gbRamSize) + { + gbMemoryMap[address>>12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + else + { + time(&gbDataMBC3.mapperLastTime); + switch(gbDataMBC3.mapperClockRegister) + { + case 0x08: + gbDataMBC3.mapperSeconds = value; + break; + case 0x09: + gbDataMBC3.mapperMinutes = value; + break; + case 0x0a: + gbDataMBC3.mapperHours = value; + break; + case 0x0b: + gbDataMBC3.mapperDays = value; + break; + case 0x0c: + if(gbDataMBC3.mapperControl & 0x80) + gbDataMBC3.mapperControl = 0x80 | value; + else + gbDataMBC3.mapperControl = value; + break; + } + } + } +} + +// MBC3 read RAM +u8 mapperMBC3ReadRAM(u16 address) +{ + if(gbDataMBC3.mapperRAMEnable) + { + if(gbDataMBC3.mapperRAMBank != -1) + { + return gbMemoryMap[address>>12][address & 0x0fff]; + } + + switch(gbDataMBC3.mapperClockRegister) + { + case 0x08: + return gbDataMBC3.mapperLSeconds; + break; + case 0x09: + return gbDataMBC3.mapperLMinutes; + break; + case 0x0a: + return gbDataMBC3.mapperLHours; + break; + case 0x0b: + return gbDataMBC3.mapperLDays; + break; + case 0x0c: + return gbDataMBC3.mapperLControl; + } + } + return 0; +} + +void memoryUpdateMapMBC3() +{ + int tmpAddress = gbDataMBC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbDataMBC3.mapperRAMBank >= 0 && gbRamSize) + { + tmpAddress = gbDataMBC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC5 gbDataMBC5 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // ROM high address + 0, // RAM address + 0 // is rumble cartridge? + }; + +// MBC5 ROM write registers +void mapperMBC5ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) + { + case 0x0000: // RAM enable register + gbDataMBC5.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + if(address < 0x3000) + { + value = value & 0xff; + if(value == gbDataMBC5.mapperROMBank) + break; + + tmpAddress = (value << 14) | (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + } + else + { + value = value & 1; + if(value == gbDataMBC5.mapperROMHighAddress) + break; + + tmpAddress = (gbDataMBC5.mapperROMBank << 14) | (value << 22); + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMHighAddress = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x4000: // RAM bank select + if(gbDataMBC5.isRumbleCartridge) + value &= 0x07; + else + value &= 0x0f; + if(value == gbDataMBC5.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) + { + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + + gbDataMBC5.mapperRAMBank = value; + gbDataMBC5.mapperRAMAddress = tmpAddress; + } + break; + } +} + +// MBC5 RAM write +void mapperMBC5RAM(u16 address, u8 value) +{ + if(gbDataMBC5.mapperRAMEnable) + { + if(gbRamSize) + { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMBC5() +{ + int tmpAddress = (gbDataMBC5.mapperROMBank << 14) | + (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) + { + tmpAddress = gbDataMBC5.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC7 gbDataMBC7 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // chip select + 0, // ?? + 0, // mapper state + 0, // buffer for receiving serial data + 0, // idle state + 0, // count of bits received + 0, // command received + 0, // address received + 0, // write enable + 0, // value to return on ram + }; + +// MBC7 ROM write registers +void mapperMBC7ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) + { + case 0x0000: + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + + if(value == gbDataMBC7.mapperROMBank) + break; + + tmpAddress = (value << 14); + + tmpAddress &= gbRomSizeMask; + gbDataMBC7.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select/enable + if(value < 8) + { + tmpAddress = (value&3) << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + + gbDataMBC7.mapperRAMBank = value; + gbDataMBC7.mapperRAMAddress = tmpAddress; + gbDataMBC7.mapperRAMEnable = 0; + } + else + { + gbDataMBC7.mapperRAMEnable = 0; + } + break; + } +} + +// MBC7 read RAM +u8 mapperMBC7ReadRAM(u16 address) +{ + switch(address & 0xa0f0) + { + case 0xa000: + case 0xa010: + case 0xa060: + case 0xa070: + return 0; + case 0xa020: + // sensor X low byte + return systemGetSensorX() & 255; + case 0xa030: + // sensor X high byte + return systemGetSensorX() >> 8; + case 0xa040: + // sensor Y low byte + return systemGetSensorY() & 255; + case 0xa050: + // sensor Y high byte + return systemGetSensorY() >> 8; + case 0xa080: + return gbDataMBC7.value; + } + return 0xff; +} + +// MBC7 RAM write +void mapperMBC7RAM(u16 address, u8 value) +{ + if(address == 0xa080) + { + // special processing needed + int oldCs = gbDataMBC7.cs,oldSk=gbDataMBC7.sk; + + gbDataMBC7.cs=value>>7; + gbDataMBC7.sk=(value>>6)&1; + + if(!oldCs && gbDataMBC7.cs) + { + if(gbDataMBC7.state==5) + { + if(gbDataMBC7.writeEnable) + { + gbMemory[0xa000+gbDataMBC7.address*2]=gbDataMBC7.buffer>>8; + gbMemory[0xa000+gbDataMBC7.address*2+1]=gbDataMBC7.buffer&0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=0; + gbDataMBC7.value=1; + } + else + { + gbDataMBC7.idle=true; + gbDataMBC7.state=0; + } + } + + if(!oldSk && gbDataMBC7.sk) + { + if(gbDataMBC7.idle) + { + if(value & 0x02) + { + gbDataMBC7.idle=false; + gbDataMBC7.count=0; + gbDataMBC7.state=1; + } + } + else + { + switch(gbDataMBC7.state) + { + case 1: + // receiving command + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value & 0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==2) + { + // finished receiving command + gbDataMBC7.state=2; + gbDataMBC7.count=0; + gbDataMBC7.code=gbDataMBC7.buffer & 3; + } + break; + case 2: + // receive address + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==8) + { + // finish receiving + gbDataMBC7.state=3; + gbDataMBC7.count=0; + gbDataMBC7.address=gbDataMBC7.buffer&0xff; + if(gbDataMBC7.code==0) + { + if((gbDataMBC7.address>>6)==0) + { + gbDataMBC7.writeEnable=0; + gbDataMBC7.state=0; + } + else if((gbDataMBC7.address>>6) == 3) + { + gbDataMBC7.writeEnable=1; + gbDataMBC7.state=0; + } + } + } + break; + case 3: + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + + switch(gbDataMBC7.code) + { + case 0: + if(gbDataMBC7.count==16) + { + if((gbDataMBC7.address>>6)==0) + { + gbDataMBC7.writeEnable = 0; + gbDataMBC7.state=0; + } + else if((gbDataMBC7.address>>6)==1) + { + if (gbDataMBC7.writeEnable) + { + for(int i=0;i<256;i++) + { + gbMemory[0xa000+i*2] = gbDataMBC7.buffer >> 8; + gbMemory[0xa000+i*2+1] = gbDataMBC7.buffer & 0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + gbDataMBC7.state=5; + } + else if((gbDataMBC7.address>>6) == 2) + { + if (gbDataMBC7.writeEnable) + { + for(int i=0;i<256;i++) + WRITE16LE((u16 *)&gbMemory[0xa000+i*2], 0xffff); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=5; + } + else if((gbDataMBC7.address>>6)==3) + { + gbDataMBC7.writeEnable = 1; + gbDataMBC7.state=0; + } + gbDataMBC7.count=0; + } + break; + case 1: + if(gbDataMBC7.count==16) + { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + } + break; + case 2: + if(gbDataMBC7.count==1) + { + gbDataMBC7.state=4; + gbDataMBC7.count=0; + gbDataMBC7.buffer = (gbMemory[0xa000+gbDataMBC7.address*2]<<8)| + (gbMemory[0xa000+gbDataMBC7.address*2+1]); + } + break; + case 3: + if(gbDataMBC7.count==16) + { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + gbDataMBC7.buffer=0xffff; + } + break; + } + break; + } + } + } + + if (oldSk && !gbDataMBC7.sk) + { + if (gbDataMBC7.state==4) + { + gbDataMBC7.value = (gbDataMBC7.buffer & 0x8000)?1:0; + gbDataMBC7.buffer <<= 1; + gbDataMBC7.count++; + if (gbDataMBC7.count==16) + { + gbDataMBC7.count=0; + gbDataMBC7.state=0; + } + } + } + } +} + +void memoryUpdateMapMBC7() +{ + int tmpAddress = (gbDataMBC5.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperHuC1 gbDataHuC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0 // RAM address + }; + +// HuC1 ROM write registers +void mapperHuC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) + { + case 0x0000: // RAM enable register + gbDataHuC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x3f; + if(value == 0) + value = 1; + if(value == gbDataHuC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataHuC1.mapperMemoryModel == 1) + { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataHuC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC1.mapperRAMBank = value; + gbDataHuC1.mapperRAMAddress = tmpAddress; + } + else + { + // 16/8, set the high address + gbDataHuC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataHuC1.mapperROMBank << 14; + tmpAddress |= (gbDataHuC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataHuC1.mapperMemoryModel = value & 1; + break; + } +} + +// HuC1 RAM write +void mapperHuC1RAM(u16 address, u8 value) +{ + if(gbDataHuC1.mapperRAMEnable) + { + if(gbRamSize) + { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapHuC1() +{ + int tmpAddress = gbDataHuC1.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) + { + tmpAddress = gbDataHuC1.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperHuC3 gbDataHuC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // RAM flag + 0 // RAM read value + }; + + +// HuC3 ROM write registers +void mapperHuC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) + { + case 0x0000: // RAM enable register + gbDataHuC3.mapperRAMEnable = ( value == 0x0a ? 1 : 0); + gbDataHuC3.mapperRAMFlag = value; + if(gbDataHuC3.mapperRAMFlag != 0x0a) + gbDataHuC3.mapperRAMBank = -1; + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataHuC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + value = value & 0x03; + if(value == gbDataHuC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC3.mapperRAMBank = value; + gbDataHuC3.mapperRAMAddress = tmpAddress; + break; + case 0x6000: // nothing to do! + break; + } +} + +// HuC3 read RAM +u8 mapperHuC3ReadRAM(u16 address) +{ + if(gbDataHuC3.mapperRAMFlag > 0x0b && + gbDataHuC3.mapperRAMFlag < 0x0e) + { + if(gbDataHuC3.mapperRAMFlag != 0x0c) + return 1; + return gbDataHuC3.mapperRAMValue; + } + else + return gbMemoryMap[address >> 12][address & 0x0fff]; +} + +// HuC3 RAM write +void mapperHuC3RAM(u16 address, u8 value) +{ + int *p; + + if(gbDataHuC3.mapperRAMFlag < 0x0b || + gbDataHuC3.mapperRAMFlag > 0x0e) + { + if(gbDataHuC3.mapperRAMEnable) + { + if(gbRamSize) + { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + } + else + { + if(gbDataHuC3.mapperRAMFlag == 0x0b) + { + if(value == 0x62) + { + gbDataHuC3.mapperRAMValue = 1; + } + else + { + switch(value & 0xf0) + { + case 0x10: + p = &gbDataHuC3.mapperRegister2; + gbDataHuC3.mapperRAMValue = *(p+gbDataHuC3.mapperRegister1++); + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + break; + case 0x30: + p = &gbDataHuC3.mapperRegister2; + *(p+gbDataHuC3.mapperRegister1++) = value & 0x0f; + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + gbDataHuC3.mapperAddress = + (gbDataHuC3.mapperRegister6 << 24) | + (gbDataHuC3.mapperRegister5 << 16) | + (gbDataHuC3.mapperRegister4 << 8) | + (gbDataHuC3.mapperRegister3 << 4) | + (gbDataHuC3.mapperRegister2); + break; + case 0x40: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0xf0) | + (value & 0x0f); + gbDataHuC3.mapperRegister2 = (gbDataHuC3.mapperAddress & 0x0f); + gbDataHuC3.mapperRegister3 = ((gbDataHuC3.mapperAddress>>4)&0x0f); + gbDataHuC3.mapperRegister4 = ((gbDataHuC3.mapperAddress>>8)&0x0f); + gbDataHuC3.mapperRegister5 = ((gbDataHuC3.mapperAddress>>16)&0x0f); + gbDataHuC3.mapperRegister6 = ((gbDataHuC3.mapperAddress>>24)&0x0f); + gbDataHuC3.mapperRegister7 = 0; + gbDataHuC3.mapperRegister8 = 0; + gbDataHuC3.mapperRAMValue = 0; + break; + case 0x50: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0x0f) | + ((value << 4)&0x0f); + break; + default: + gbDataHuC3.mapperRAMValue = 1; + break; + } + } + } + } +} + +void memoryUpdateMapHuC3() +{ + int tmpAddress = gbDataHuC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) + { + tmpAddress = gbDataHuC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} diff --git a/src/gb/gbMemory.h b/src/gb/gbMemory.h new file mode 100644 index 0000000..b3d9695 --- /dev/null +++ b/src/gb/gbMemory.h @@ -0,0 +1,156 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +struct mapperMBC1 + { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; + }; + +struct mapperMBC2 + { + int mapperRAMEnable; + int mapperROMBank; + }; + +struct mapperMBC3 + { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperClockLatch; + int mapperClockRegister; + int mapperSeconds; + int mapperMinutes; + int mapperHours; + int mapperDays; + int mapperControl; + int mapperLSeconds; + int mapperLMinutes; + int mapperLHours; + int mapperLDays; + int mapperLControl; + time_t mapperLastTime; + }; + +struct mapperMBC5 + { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperROMHighAddress; + int mapperRAMAddress; + int isRumbleCartridge; + }; + +struct mapperMBC7 + { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int cs; + int sk; + int state; + int buffer; + int idle; + int count; + int code; + int address; + int writeEnable; + int value; + }; + +struct mapperHuC1 + { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; + }; + +struct mapperHuC3 + { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperAddress; + int mapperRAMFlag; + int mapperRAMValue; + int mapperRegister1; + int mapperRegister2; + int mapperRegister3; + int mapperRegister4; + int mapperRegister5; + int mapperRegister6; + int mapperRegister7; + int mapperRegister8; + }; + +extern mapperMBC1 gbDataMBC1; +extern mapperMBC2 gbDataMBC2; +extern mapperMBC3 gbDataMBC3; +extern mapperMBC5 gbDataMBC5; +extern mapperHuC1 gbDataHuC1; +extern mapperHuC3 gbDataHuC3; + +void mapperMBC1ROM(u16,u8); +void mapperMBC1RAM(u16,u8); +void mapperMBC2ROM(u16,u8); +void mapperMBC2RAM(u16,u8); +void mapperMBC3ROM(u16,u8); +void mapperMBC3RAM(u16,u8); +u8 mapperMBC3ReadRAM(u16); +void mapperMBC5ROM(u16,u8); +void mapperMBC5RAM(u16,u8); +void mapperMBC7ROM(u16,u8); +void mapperMBC7RAM(u16,u8); +u8 mapperMBC7ReadRAM(u16); +void mapperHuC1ROM(u16,u8); +void mapperHuC1RAM(u16,u8); +void mapperHuC3ROM(u16,u8); +void mapperHuC3RAM(u16,u8); +u8 mapperHuC3ReadRAM(u16); + +//extern void (*mapper)(u16,u8); +//extern void (*mapperRAM)(u16,u8); +//extern u8 (*mapperReadRAM)(u16); + +extern void memoryUpdateMapMBC1(); +extern void memoryUpdateMapMBC2(); +extern void memoryUpdateMapMBC3(); +extern void memoryUpdateMapMBC5(); +extern void memoryUpdateMapMBC7(); +extern void memoryUpdateMapHuC1(); +extern void memoryUpdateMapHuC3(); + + + + + diff --git a/src/gb/gbPrinter.cpp b/src/gb/gbPrinter.cpp new file mode 100644 index 0000000..8bb1b66 --- /dev/null +++ b/src/gb/gbPrinter.cpp @@ -0,0 +1,249 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include "../GBA.h" + +u8 gbPrinterStatus = 0; +int gbPrinterState = 0; +u8 gbPrinterData[0x280*9]; +u8 gbPrinterPacket[0x400]; +int gbPrinterCount = 0; +int gbPrinterDataCount = 0; +int gbPrinterDataSize = 0; +int gbPrinterResult = 0; + +bool gbPrinterCheckCRC() +{ + u16 crc = 0; + + for(int i = 2; i < (6+gbPrinterDataSize); i++) + { + crc += gbPrinterPacket[i]; + } + + int msgCrc = gbPrinterPacket[6+gbPrinterDataSize] + + (gbPrinterPacket[7+gbPrinterDataSize]<<8); + + return msgCrc == crc; +} + +void gbPrinterReset() +{ + gbPrinterState = 0; + gbPrinterDataSize = 0; + gbPrinterDataCount = 0; + gbPrinterCount = 0; + gbPrinterStatus = 0; + gbPrinterResult = 0; +} + +void gbPrinterShowData() +{ + systemGbPrint(gbPrinterData, + gbPrinterPacket[6], + gbPrinterPacket[7], + gbPrinterPacket[8], + gbPrinterPacket[9]); + /* + allegro_init(); + install_keyboard(); + set_gfx_mode(GFX_AUTODETECT, 160, 144, 0, 0); + PALETTE pal; + pal[0].r = 255; + pal[0].g = 255; + pal[0].b = 255; + pal[1].r = 168; + pal[1].g = 168; + pal[1].b = 168; + pal[2].r = 96; + pal[2].g = 96; + pal[2].b = 96; + pal[3].r = 0; + pal[3].g = 0; + pal[3].b = 0; + set_palette(pal); + acquire_screen(); + u8 *data = gbPrinterData; + for(int y = 0; y < 0x12; y++) { + for(int x = 0; x < 0x14; x++) { + for(int k = 0; k < 8; k++) { + int a = *data++; + int b = *data++; + for(int j = 0; j < 8; j++) { + int mask = 1 << (7-j); + int c = 0; + if(a & mask) + c++; + if(b & mask) + c+=2; + putpixel(screen, x*8+j, y*8+k, c); + } + } + } + } + release_screen(); + while(!keypressed()) { + } + */ +} + +void gbPrinterReceiveData() +{ + if(gbPrinterPacket[3]) + { // compressed + u8 *data = &gbPrinterPacket[6]; + u8 *dest = &gbPrinterData[gbPrinterDataCount]; + int len = 0; + while(len < gbPrinterDataSize) + { + u8 control = *data++; + if(control & 0x80) + { // repeated data + control &= 0x7f; + control += 2; + memset(dest, *data++, control); + len += control; + dest += control; + } + else + { // raw data + control++; + memcpy(dest, data, control); + dest += control; + data += control; + len += control; + } + } + } + else + { + memcpy(&gbPrinterData[gbPrinterDataCount], + &gbPrinterPacket[6], + gbPrinterDataSize); + gbPrinterDataCount += gbPrinterDataSize; + } +} + +void gbPrinterCommand() +{ + switch(gbPrinterPacket[2]) + { + case 0x01: + // reset/initialize packet + gbPrinterDataCount = 0; + gbPrinterStatus = 0; + break; + case 0x02: + // print packet + gbPrinterShowData(); + break; + case 0x04: + // data packet + gbPrinterReceiveData(); + break; + case 0x0f: + // NUL packet + break; + } +} + +u8 gbPrinterSend(u8 b) +{ + switch(gbPrinterState) + { + case 0: + gbPrinterCount = 0; + // receiving preamble + if(b == 0x88) + { + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + } + else + { + // todo: handle failure + gbPrinterReset(); + } + break; + case 1: + // receiving preamble + if(b == 0x33) + { + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + } + else + { + // todo: handle failure + gbPrinterReset(); + } + break; + case 2: + // receiving header + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCount == 6) + { + gbPrinterState++; + gbPrinterDataSize = gbPrinterPacket[4] + (gbPrinterPacket[5]<<8); + } + break; + case 3: + // receiving data + if(gbPrinterDataSize) + { + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCount == (6+gbPrinterDataSize)) + { + gbPrinterState++; + } + break; + } + gbPrinterState++; + // intentionally move to next if no data to receive + case 4: + // receiving CRC + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + break; + case 5: + // receiving CRC-2 + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCheckCRC()) + { + gbPrinterCommand(); + } + gbPrinterState++; + break; + case 6: + // receiving dummy 1 + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterResult = 0x81; + gbPrinterState++; + break; + case 7: + // receiving dummy 2 + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterResult = gbPrinterStatus; + gbPrinterState = 0; + gbPrinterCount = 0; + break; + } + return gbPrinterResult; +} diff --git a/src/gb/gbPrinter.h b/src/gb/gbPrinter.h new file mode 100644 index 0000000..9bebd4e --- /dev/null +++ b/src/gb/gbPrinter.h @@ -0,0 +1,20 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +extern u8 gbPrinterSend(u8 b); diff --git a/src/gb/gbSGB.cpp b/src/gb/gbSGB.cpp new file mode 100644 index 0000000..25a7ae5 --- /dev/null +++ b/src/gb/gbSGB.cpp @@ -0,0 +1,1014 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "../System.h" +#include "../Port.h" +#include "../Util.h" +#include "GB.h" +#include "gbGlobals.h" + +extern u8 *pix; +extern bool speedup; + +#define GBSGB_NONE 0 +#define GBSGB_RESET 1 +#define GBSGB_PACKET_TRANSMIT 2 + +u8 *gbSgbBorderChar = NULL; +u8 *gbSgbBorder = NULL; + +int gbSgbCGBSupport = 0; +int gbSgbMask = 0; +int gbSgbMode = 0; +int gbSgbPacketState = GBSGB_NONE; +int gbSgbBit = 0; +int gbSgbPacketTimeout = 0; +int GBSGB_PACKET_TIMEOUT = 66666; +u8 gbSgbPacket[16*7]; +int gbSgbPacketNBits = 0; +int gbSgbPacketByte = 0; +int gbSgbPacketNumber = 0; +int gbSgbMultiplayer = 0; +int gbSgbFourPlayers = 0; +u8 gbSgbNextController = 0x0f; +u8 gbSgbReadingController = 0; +u16 gbSgbSCPPalette[4*512]; +u8 gbSgbATF[20 * 18]; +u8 gbSgbATFList[45 * 20 * 18]; +u8 gbSgbScreenBuffer[4160]; + +inline void gbSgbDraw24Bit(u8 *p, u16 v) +{ + *((u32*) p) = systemColorMap32[v]; +} + +inline void gbSgbDraw32Bit(u32 *p, u16 v) +{ + *p = systemColorMap32[v]; +} + +inline void gbSgbDraw16Bit(u16 *p, u16 v) +{ + *p = systemColorMap16[v]; +} + +void gbSgbReset() +{ + gbSgbPacketTimeout = 0; + gbSgbCGBSupport = 0; + gbSgbMask = 0; + gbSgbPacketState = GBSGB_NONE; + gbSgbBit = 0; + gbSgbPacketNBits = 0; + gbSgbPacketNumber = 0; + gbSgbMultiplayer = 0; + gbSgbFourPlayers = 0; + gbSgbNextController = 0x0f; + gbSgbReadingController = 0; + + memset(gbSgbSCPPalette, 0, 512*4); + memset(gbSgbATF, 0, 20*18); + memset(gbSgbATFList, 0, 45 * 20 * 18); + memset(gbSgbPacket, 0, 16 * 7); + memset(gbSgbBorderChar, 0, 32*256); + memset(gbSgbBorder, 0, 2048); + + int i; + for(i = 1; i < 2048; i+=2) + { + gbSgbBorder[i] = 1 << 2; + } + + for(i = 0; i < 4; i++) + { + gbPalette[i*4] = (0x1f) | (0x1f << 5) | (0x1f << 10); + gbPalette[i*4+1] = (0x15) | (0x15 << 5) | (0x15 << 10); + gbPalette[i*4+2] = (0x0c) | (0x0c << 5) | (0x0c << 10); + gbPalette[i*4+3] = 0; + } +} + +void gbSgbInit() +{ + gbSgbBorderChar = (u8 *)malloc(32 * 256); + gbSgbBorder = (u8 *)malloc(2048); + + gbSgbReset(); +} + +void gbSgbShutdown() +{ + if(gbSgbBorderChar != NULL) + { + free(gbSgbBorderChar); + gbSgbBorderChar = NULL; + } + + if(gbSgbBorder != NULL) + { + free(gbSgbBorder); + gbSgbBorder = NULL; + } +} + +void gbSgbFillScreen(u16 color) +{ + switch(systemColorDepth) + { + case 16: + { + for(int y = 0; y < 144; y++) + { + int yLine = (y+gbBorderRowSkip+1)*(gbBorderLineSkip+2) + + gbBorderColumnSkip; + u16 *dest = (u16*)pix + yLine; + for(register int x = 0; x < 160; x++) + gbSgbDraw16Bit(dest++, color); + } + } + break; + case 24: + { + for(int y = 0; y < 144; y++) + { + int yLine = (y+gbBorderRowSkip)*gbBorderLineSkip + gbBorderColumnSkip; + u8 *dest = (u8 *)pix + yLine*3; + for(register int x = 0; x < 160; x++) + { + gbSgbDraw24Bit(dest, color); + dest += 3; + } + } + } + break; + case 32: + { + for(int y = 0; y < 144; y++) + { + int yLine = (y+gbBorderRowSkip+1)*(gbBorderLineSkip+1) + gbBorderColumnSkip; + u32 *dest = (u32 *)pix + yLine; + for(register int x = 0; x < 160; x++) + { + gbSgbDraw32Bit(dest++, color); + } + } + } + break; + } +} + +#define getmem(x) gbMemoryMap[(x) >> 12][(x) & 0xfff] + +void gbSgbRenderScreenToBuffer() +{ + u16 mapAddress = 0x9800; + + if(register_LCDC & 0x08) + mapAddress = 0x9c00; + + u16 patternAddress = 0x8800; + + int flag = 1; + + if(register_LCDC & 0x10) + { + patternAddress = 0x8000; + flag = 0; + } + + u8 *toAddress = gbSgbScreenBuffer; + + for(int i = 0; i < 13; i++) + { + for(int j = 0; j < 20; j++) + { + int tile = getmem(mapAddress); + mapAddress++; + + if(flag) + { + if(tile > 127) + tile -= 128; + else + tile += 128; + } + for(int k = 0; k < 16; k++) + *toAddress++ = getmem(patternAddress + tile*16 + k); + } + mapAddress += 12; + } +} + +void gbSgbDrawBorderTile(int x, int y, int tile, int attr) +{ + u16 *dest = (u16*)pix + ((y+1) * (256+2)) + x; + u8 *dest8 = (u8*)pix + ((y*256)+x)*3; + u32 *dest32 = (u32*)pix + ((y+1)*257) + x; + + u8 *tileAddress = &gbSgbBorderChar[tile * 32]; + u8 *tileAddress2 = &gbSgbBorderChar[tile * 32 + 16]; + + u8 l = 8; + + u8 palette = ((attr >> 2 ) & 7); + + if(palette < 4) + palette += 4; + + palette *= 16; + + u8 xx = 0; + u8 yy = 0; + + int flipX = attr & 0x40; + int flipY = attr & 0x80; + + while(l > 0) + { + u8 mask = 0x80; + u8 a = *tileAddress++; + u8 b = *tileAddress++; + u8 c = *tileAddress2++; + u8 d = *tileAddress2++; + + while(mask > 0) + { + + u8 color = 0; + if(a & mask) + color++; + if(b & mask) + color+=2; + if(c & mask) + color+=4; + if(d & mask) + color+=8; + + u8 xxx = xx; + u8 yyy = yy; + + if(flipX) + xxx = 7 - xx; + if(flipY) + yyy = 7 - yy; + + u16 c = gbPalette[palette + color]; + if(!color) + c = gbPalette[0]; + if((yy < 40 || yy >= 184) || (xx < 48 || xx >= 208)) + { + switch(systemColorDepth) + { + case 16: + gbSgbDraw16Bit(dest + yyy*(256+2) + xxx, c); + break; + case 24: + gbSgbDraw24Bit(dest8 + (yyy*256+xxx)*3, c); + break; + case 32: + gbSgbDraw32Bit(dest32 + yyy*(256+1)+xxx, c); + break; + } + } + + mask >>= 1; + + xx++; + } + yy++; + xx = 0; + l--; + mask = 0x80; + } +} + +void gbSgbRenderBorder() +{ + if(gbBorderOn) + { + u8 *fromAddress = gbSgbBorder; + + for(u8 y = 0; y < 28; y++) + { + for(u8 x = 0; x< 32; x++) + { + u8 tile = *fromAddress++; + u8 attr = *fromAddress++; + + gbSgbDrawBorderTile(x*8,y*8,tile,attr); + } + } + } +} + +void gbSgbPicture() +{ + gbSgbRenderScreenToBuffer(); + + memcpy(gbSgbBorder, gbSgbScreenBuffer, 2048); + + u16 *paletteAddr = (u16 *)&gbSgbScreenBuffer[2048]; + + for(int i = 64; i < 128; i++) + { + gbPalette[i] = READ16LE(paletteAddr++); + } + + gbSgbCGBSupport |= 4; + + if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) + { + gbBorderOn = 1; + systemGbBorderOn(); + } + + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); + + if(gbSgbMode && gbCgbMode && gbSgbCGBSupport > 4) + { + gbSgbCGBSupport = 0; + gbSgbMode = 0; + gbSgbMask = 0; + gbSgbRenderBorder(); + gbReset(); + } + + if(gbSgbCGBSupport > 4) + gbSgbCGBSupport = 0; +} + +void gbSgbSetPalette(int a,int b,u16 *p) +{ + u16 bit00 = READ16LE(p++); + int i; + + for(i = 1; i < 4; i++) + { + gbPalette[a*4+i] = READ16LE(p++); + } + + for(i = 1; i < 4; i++) + { + gbPalette[b*4+i] = READ16LE(p++); + } + + gbPalette[0] = gbPalette[4] = gbPalette[8] = gbPalette[12] = bit00; + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); +} + +void gbSgbScpPalette() +{ + gbSgbRenderScreenToBuffer(); + + u16 *fromAddress = (u16 *)gbSgbScreenBuffer; + + for(int i = 0; i < 512*4; i++) + { + gbSgbSCPPalette[i] = READ16LE(fromAddress++); + } +} + +void gbSgbSetATF(int n) +{ + if(n < 0) + n = 0; + if(n > 44) + n = 44; + memcpy(gbSgbATF,&gbSgbATFList[n * 20 * 18], 20 * 18); + + if(gbSgbPacket[1] & 0x40) + { + gbSgbMask = 0; + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbSetPalette() +{ + u16 pal = READ16LE((((u16 *)&gbSgbPacket[1])))&511; + memcpy(&gbPalette[0], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[3])))&511; + memcpy(&gbPalette[4], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[5])))&511; + memcpy(&gbPalette[8], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[7])))&511; + memcpy(&gbPalette[12], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + u8 atf = gbSgbPacket[9]; + + if(atf & 0x80) + { + gbSgbSetATF(atf & 0x3f); + } + + if(atf & 0x40) + { + gbSgbMask = 0; + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbAttributeBlock() +{ + u8 *fromAddress = &gbSgbPacket[1]; + + u8 nDataSet = *fromAddress++; + if(nDataSet > 12) + nDataSet = 12; + if(nDataSet == 0) + nDataSet = 1; + + while(nDataSet) + { + u8 controlCode = (*fromAddress++) & 7; + u8 paletteDesignation = (*fromAddress++) & 0x3f; + u8 startH = (*fromAddress++) & 0x1f; + u8 startV = (*fromAddress++) & 0x1f; + u8 endH = (*fromAddress++) & 0x1f; + u8 endV = (*fromAddress++) & 0x1f; + + u8 * toAddress = gbSgbATF; + + for(u8 y = 0; y < 18; y++) + { + for(u8 x = 0; x < 20; x++) + { + if(x < startH || y < startV || + x > endH || y > endV) + { + // outside + if(controlCode & 0x04) + *toAddress = (paletteDesignation >> 4) & 0x03; + } + else if(x > startH && x < endH && + y > startV && y < endV) + { + // inside + if(controlCode & 0x01) + *toAddress = paletteDesignation & 0x03; + } + else + { + // surrounding line + if(controlCode & 0x02) + *toAddress = (paletteDesignation>>2) & 0x03; + else if(controlCode == 0x01) + *toAddress = paletteDesignation & 0x03; + } + toAddress++; + } + } + nDataSet--; + } +} + +void gbSgbSetColumnPalette(u8 col, u8 p) +{ + // if(col < 0) + // col = 0; + if(col > 19) + col = 19; + + p &= 3; + + u8 *toAddress = &gbSgbATF[col]; + + for(u8 y = 0; y < 18; y++) + { + *toAddress = p; + toAddress += 20; + } +} + +void gbSgbSetRowPalette(u8 row, u8 p) +{ + // if(row < 0) + // row = 0; + if(row > 17) + row = 17; + + p &= 3; + + u8 *toAddress = &gbSgbATF[row*20]; + + for(u8 x = 0; x < 20; x++) + { + *toAddress++ = p; + } +} + +void gbSgbAttributeDivide() +{ + u8 control = gbSgbPacket[1]; + u8 coord = gbSgbPacket[2]; + u8 colorBR = control & 3; + u8 colorAL = (control >> 2) & 3; + u8 colorOL = (control >> 4) & 3; + + if(control & 0x40) + { + if(coord > 17) + coord = 17; + + for(u8 i = 0; i < 18; i++) + { + if(i < coord) + gbSgbSetRowPalette(i, colorAL); + else if ( i > coord) + gbSgbSetRowPalette(i, colorBR); + else + gbSgbSetRowPalette(i, colorOL); + } + } + else + { + if(coord > 19) + coord = 19; + + for(u8 i = 0; i < 20; i++) + { + if(i < coord) + gbSgbSetColumnPalette(i, colorAL); + else if ( i > coord) + gbSgbSetColumnPalette(i, colorBR); + else + gbSgbSetColumnPalette(i, colorOL); + } + } +} + +void gbSgbAttributeLine() +{ + u8 *fromAddress = &gbSgbPacket[1]; + + u8 nDataSet = *fromAddress++; + + if(nDataSet > 0x6e) + nDataSet = 0x6e; + + while(nDataSet) + { + u8 line = *fromAddress++; + u8 num = line & 0x1f; + u8 pal = (line >> 5) & 0x03; + if(line & 0x80) + { + if(num > 17) + num = 17; + gbSgbSetRowPalette(num,pal); + } + else + { + if(num > 19) + num = 19; + gbSgbSetColumnPalette(num,pal); + } + nDataSet--; + } +} + +void gbSgbAttributeCharacter() +{ + u8 startH = gbSgbPacket[1] & 0x1f; + u8 startV = gbSgbPacket[2] & 0x1f; + int nDataSet = READ16LE(((u16 *)&gbSgbPacket[3])); + int style = gbSgbPacket[5] & 1; + if(startH > 19) + startH = 19; + if(startV > 17) + startV = 17; + + u8 s = 6; + u8 *fromAddress = &gbSgbPacket[6]; + u8 v = *fromAddress++; + + if(style) + { + while(nDataSet) + { + u8 p = (v >> s) & 3; + gbSgbATF[startV * 20 + startH] = p; + startV++; + if(startV == 18) + { + startV = 0; + startH++; + if(startH == 20) + break; + } + + if(s) + s -= 2; + else + { + s = 6; + v = *fromAddress++; + nDataSet--; + } + } + } + else + { + while(nDataSet) + { + u8 p = (v >> s) & 3; + gbSgbATF[startV * 20 + startH] = p; + startH++; + if(startH == 20) + { + startH = 0; + startV++; + if(startV == 18) + break; + } + + if(s) + s -= 2; + else + { + s = 6; + v = *fromAddress++; + nDataSet--; + } + } + } +} + +void gbSgbSetATFList() +{ + gbSgbRenderScreenToBuffer(); + + u8 *fromAddress = gbSgbScreenBuffer; + u8 *toAddress = gbSgbATFList; + + for(int i = 0; i < 45; i++) + { + for(int j = 0; j < 90; j++) + { + u8 v = *fromAddress++; + u8 s = 6; + if(i == 2) + s = 6; + for(int k = 0; k < 4; k++) + { + *toAddress++ = (v >> s) & 0x03; + s -= 2; + } + } + } +} + +void gbSgbMaskEnable() +{ + int gbSgbMaskFlag = gbSgbPacket[1] & 3; + + gbSgbMask = gbSgbMaskFlag; + + switch(gbSgbMaskFlag) + { + case 1: + break; + case 2: + gbSgbFillScreen(0x0000); + // memset(&gbPalette[0], 0, 128*sizeof(u16)); + break; + case 3: + gbSgbFillScreen(gbPalette[0]); + break; + } + if(!gbSgbMask) + { + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbChrTransfer() +{ + gbSgbRenderScreenToBuffer(); + + int address = (gbSgbPacket[1] & 1) * (128*32); + + if(gbSgbPacket[1] & 1) + gbSgbCGBSupport |= 2; + else + gbSgbCGBSupport |= 1; + + memcpy(&gbSgbBorderChar[address], gbSgbScreenBuffer, 128 * 32); + + if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) + { + gbBorderOn = 1; + systemGbBorderOn(); + } + + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); + + if(gbSgbMode && gbCgbMode && gbSgbCGBSupport == 7) + { + gbSgbCGBSupport = 0; + gbSgbMode = 0; + gbSgbMask = 0; + gbSgbRenderBorder(); + gbReset(); + } + + if(gbSgbCGBSupport > 4) + gbSgbCGBSupport = 0; +} + +void gbSgbMultiRequest() +{ + if(gbSgbPacket[1] & 1) + { + gbSgbMultiplayer = 1; + if(gbSgbPacket[1] & 2) + gbSgbFourPlayers = 1; + else + gbSgbFourPlayers = 0; + gbSgbNextController = 0x0e; + } + else + { + gbSgbFourPlayers = 0; + gbSgbMultiplayer = 0; + gbSgbNextController = 0x0f; + } +} + +void gbSgbCommand() +{ + int command = gbSgbPacket[0] >> 3; + // int nPacket = gbSgbPacket[0] & 7; + + switch(command) + { + case 0x00: + gbSgbSetPalette(0,1,(u16 *)&gbSgbPacket[1]); + break; + case 0x01: + gbSgbSetPalette(2,3,(u16 *)&gbSgbPacket[1]); + break; + case 0x02: + gbSgbSetPalette(0,3,(u16 *)&gbSgbPacket[1]); + break; + case 0x03: + gbSgbSetPalette(1,2,(u16 *)&gbSgbPacket[1]); + break; + case 0x04: + gbSgbAttributeBlock(); + break; + case 0x05: + gbSgbAttributeLine(); + break; + case 0x06: + gbSgbAttributeDivide(); + break; + case 0x07: + gbSgbAttributeCharacter(); + break; + case 0x0a: + gbSgbSetPalette(); + break; + case 0x0b: + gbSgbScpPalette(); + break; + case 0x11: + gbSgbMultiRequest(); + break; + case 0x13: + gbSgbChrTransfer(); + break; + case 0x14: + gbSgbPicture(); + break; + case 0x15: + gbSgbSetATFList(); + break; + case 0x16: + gbSgbSetATF(gbSgbPacket[1] & 0x3f); + break; + case 0x17: + gbSgbMaskEnable(); + break; + } +} + +void gbSgbResetPacketState() +{ + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; +} + +void gbSgbDoBitTransfer(u8 value) +{ + value = value & 0x30; + switch(gbSgbPacketState) + { + case GBSGB_NONE: + if(value == 0) + { + gbSgbPacketState = GBSGB_RESET; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + else if (value == 0x30) + { + if(gbSgbMultiplayer) + { + if((gbSgbReadingController & 7) == 7) + { + gbSgbReadingController = 0; + if(gbSgbMultiplayer) + { + gbSgbNextController--; + if(gbSgbFourPlayers) + { + if(gbSgbNextController == 0x0b) + gbSgbNextController = 0x0f; + } + else + { + if(gbSgbNextController == 0x0d) + gbSgbNextController = 0x0f; + } + } + } + else + { + gbSgbReadingController &= 3; + } + } + gbSgbPacketTimeout = 0; + } + else + { + if(value == 0x10) + gbSgbReadingController |= 0x2; + else if(value == 0x20) + gbSgbReadingController |= 0x01; + gbSgbPacketTimeout = 0; + } + gbSgbPacketTimeout = 0; + break; + case GBSGB_RESET: + if(value == 0x30) + { + gbSgbPacketState = GBSGB_PACKET_TRANSMIT; + gbSgbPacketByte = 0; + gbSgbPacketNBits = 0; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + else if(value == 0x00) + { + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + gbSgbPacketState = GBSGB_RESET; + } + else + { + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + } + break; + case GBSGB_PACKET_TRANSMIT: + if(value == 0) + { + gbSgbPacketState = GBSGB_RESET; + gbSgbPacketTimeout = 0; + } + else if (value == 0x30) + { + if(gbSgbPacketNBits == 128) + { + gbSgbPacketNBits = 0; + gbSgbPacketByte = 0; + gbSgbPacketNumber++; + gbSgbPacketTimeout = 0; + if(gbSgbPacketNumber == (gbSgbPacket[0] & 7)) + { + gbSgbCommand(); + gbSgbPacketNumber = 0; + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + } + } + else + { + if(gbSgbPacketNBits < 128) + { + gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] >>= 1; + gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] |= gbSgbBit; + gbSgbPacketNBits++; + if(!(gbSgbPacketNBits & 7)) + { + gbSgbPacketByte++; + } + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + } + } + else + { + if(value == 0x20) + gbSgbBit = 0x00; + else + gbSgbBit = 0x80; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + gbSgbReadingController = 0; + break; + default: + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + break; + } +} + +variable_desc gbSgbSaveStruct[] = { + { &gbSgbMask, sizeof(int) }, + { &gbSgbPacketState, sizeof(int) }, + { &gbSgbBit, sizeof(int) }, + { &gbSgbPacketNBits, sizeof(int) }, + { &gbSgbPacketByte, sizeof(int) }, + { &gbSgbPacketNumber, sizeof(int) }, + { &gbSgbMultiplayer, sizeof(int) }, + { &gbSgbNextController, sizeof(u8) }, + { &gbSgbReadingController, sizeof(u8) }, + { NULL, 0 } + }; + +variable_desc gbSgbSaveStructV3[] = { + { &gbSgbMask, sizeof(int) }, + { &gbSgbPacketState, sizeof(int) }, + { &gbSgbBit, sizeof(int) }, + { &gbSgbPacketNBits, sizeof(int) }, + { &gbSgbPacketByte, sizeof(int) }, + { &gbSgbPacketNumber, sizeof(int) }, + { &gbSgbMultiplayer, sizeof(int) }, + { &gbSgbNextController, sizeof(u8) }, + { &gbSgbReadingController, sizeof(u8) }, + { &gbSgbFourPlayers, sizeof(int) }, + { NULL, 0 } + }; + +void gbSgbSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, gbSgbSaveStructV3); + + utilGzWrite(gzFile, gbSgbBorder, 2048); + utilGzWrite(gzFile, gbSgbBorderChar, 32*256); + + utilGzWrite(gzFile, gbSgbPacket, 16*7); + + utilGzWrite(gzFile, gbSgbSCPPalette, 4 * 512 * sizeof(u16)); + utilGzWrite(gzFile, gbSgbATF, 20 * 18); + utilGzWrite(gzFile, gbSgbATFList, 45 * 20 * 18); +} + +void gbSgbReadGame(gzFile gzFile, int version) +{ + if(version >= 3) + utilReadData(gzFile, gbSgbSaveStructV3); + else + { + utilReadData(gzFile, gbSgbSaveStruct); + gbSgbFourPlayers = 0; + } + + if(version >= 8) + { + utilGzRead(gzFile, gbSgbBorder, 2048); + utilGzRead(gzFile, gbSgbBorderChar, 32*256); + } + + utilGzRead(gzFile, gbSgbPacket, 16*7); + + utilGzRead(gzFile, gbSgbSCPPalette, 4 * 512 * sizeof(u16)); + utilGzRead(gzFile, gbSgbATF, 20 * 18); + utilGzRead(gzFile, gbSgbATFList, 45 * 20 * 18); +} diff --git a/src/gb/gbSGB.h b/src/gb/gbSGB.h new file mode 100644 index 0000000..95afb2c --- /dev/null +++ b/src/gb/gbSGB.h @@ -0,0 +1,39 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +void gbSgbInit(); +void gbSgbShutdown(); +void gbSgbCommand(); +void gbSgbResetPacketState(); +void gbSgbReset(); +void gbSgbDoBitTransfer(u8); +void gbSgbSaveGame(gzFile); +void gbSgbReadGame(gzFile, int version); +void gbSgbRenderBorder(); + +extern u8 gbSgbATF[20*18]; +extern int gbSgbMode; +extern int gbSgbMask; +extern int gbSgbMultiplayer; +extern u8 gbSgbNextController; +extern int gbSgbPacketTimeout; +extern u8 gbSgbReadingController; +extern int gbSgbFourPlayers; + + diff --git a/src/gb/gbSound.cpp b/src/gb/gbSound.cpp new file mode 100644 index 0000000..e3e9c8f --- /dev/null +++ b/src/gb/gbSound.cpp @@ -0,0 +1,1065 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "../System.h" +#include "../Util.h" +#include "gbGlobals.h" +#include "gbSound.h" + +extern u8 soundBuffer[6][735]; +extern u16 soundFinalWave[1470]; +extern int soundVolume; + +#define SOUND_MAGIC 0x60000000 +#define SOUND_MAGIC_2 0x30000000 +#define NOISE_MAGIC 5 + +extern int speed; + +extern void soundResume(); + +extern u8 soundWavePattern[4][32]; + +extern int soundBufferLen; +extern int soundBufferTotalLen; +extern int soundQuality; +extern int soundPaused; +extern int soundPlay; +extern int soundTicks; +extern int SOUND_CLOCK_TICKS; +extern u32 soundNextPosition; + +extern int soundLevel1; +extern int soundLevel2; +extern int soundBalance; +extern int soundMasterOn; +extern int soundIndex; +extern int soundBufferIndex; +int soundVIN = 0; +extern int soundDebug; + +extern int sound1On; +extern int sound1ATL; +extern int sound1Skip; +extern int sound1Index; +extern int sound1Continue; +extern int sound1EnvelopeVolume; +extern int sound1EnvelopeATL; +extern int sound1EnvelopeUpDown; +extern int sound1EnvelopeATLReload; +extern int sound1SweepATL; +extern int sound1SweepATLReload; +extern int sound1SweepSteps; +extern int sound1SweepUpDown; +extern int sound1SweepStep; +extern u8 *sound1Wave; + +extern int sound2On; +extern int sound2ATL; +extern int sound2Skip; +extern int sound2Index; +extern int sound2Continue; +extern int sound2EnvelopeVolume; +extern int sound2EnvelopeATL; +extern int sound2EnvelopeUpDown; +extern int sound2EnvelopeATLReload; +extern u8 *sound2Wave; + +extern int sound3On; +extern int sound3ATL; +extern int sound3Skip; +extern int sound3Index; +extern int sound3Continue; +extern int sound3OutputLevel; +extern int sound3Last; + +extern int sound4On; +extern int sound4Clock; +extern int sound4ATL; +extern int sound4Skip; +extern int sound4Index; +extern int sound4ShiftRight; +extern int sound4ShiftSkip; +extern int sound4ShiftIndex; +extern int sound4NSteps; +extern int sound4CountDown; +extern int sound4Continue; +extern int sound4EnvelopeVolume; +extern int sound4EnvelopeATL; +extern int sound4EnvelopeUpDown; +extern int sound4EnvelopeATLReload; + +extern int soundEnableFlag; + +extern int soundFreqRatio[8]; +extern int soundShiftClock[16]; + +extern s16 soundFilter[4000]; +extern s16 soundLeft[5]; +extern s16 soundRight[5]; +extern int soundEchoIndex; +extern bool soundEcho; +extern bool soundLowPass; +extern bool soundReverse; +extern bool soundOffFlag; + +bool gbDigitalSound = false; + +void gbSoundEvent(register u16 address, register int data) +{ + int freq = 0; + + gbMemory[address] = data; + +#ifndef FINAL_VERSION + if(soundDebug) + { + // don't translate. debug only + log("Sound event: %08lx %02x\n", address, data); + } +#endif + switch(address) + { + case NR10: + sound1SweepATL = sound1SweepATLReload = 344 * ((data >> 4) & 7); + sound1SweepSteps = data & 7; + sound1SweepUpDown = data & 0x08; + sound1SweepStep = 0; + break; + case NR11: + sound1Wave = soundWavePattern[data >> 6]; + sound1ATL = 172 * (64 - (data & 0x3f)); + break; + case NR12: + sound1EnvelopeVolume = data >> 4; + sound1EnvelopeUpDown = data & 0x08; + sound1EnvelopeATLReload = sound1EnvelopeATL = 689 * (data & 7); + break; + case NR13: + freq = (((int)(gbMemory[NR14] & 7)) << 8) | data; + sound1ATL = 172 * (64 - (gbMemory[NR11] & 0x3f)); + freq = 2048 - freq; + if(freq) + { + sound1Skip = SOUND_MAGIC / freq; + } + else + sound1Skip = 0; + break; + case NR14: + freq = (((int)(data&7) << 8) | gbMemory[NR13]); + freq = 2048 - freq; + sound1ATL = 172 * (64 - (gbMemory[NR11] & 0x3f)); + sound1Continue = data & 0x40; + if(freq) + { + sound1Skip = SOUND_MAGIC / freq; + } + else + sound1Skip = 0; + if(data & 0x80) + { + gbMemory[NR52] |= 1; + sound1EnvelopeVolume = gbMemory[NR12] >> 4; + sound1EnvelopeUpDown = gbMemory[NR12] & 0x08; + sound1ATL = 172 * (64 - (gbMemory[NR11] & 0x3f)); + sound1EnvelopeATLReload = sound1EnvelopeATL = 689 * (gbMemory[NR12] & 7); + sound1SweepATL = sound1SweepATLReload = 344 * ((gbMemory[NR10] >> 4) & 7); + sound1SweepSteps = gbMemory[NR10] & 7; + sound1SweepUpDown = gbMemory[NR10] & 0x08; + sound1SweepStep = 0; + + sound1Index = 0; + sound1On = 1; + } + break; + case NR21: + sound2Wave = soundWavePattern[data >> 6]; + sound2ATL = 172 * (64 - (data & 0x3f)); + break; + case NR22: + sound2EnvelopeVolume = data >> 4; + sound2EnvelopeUpDown = data & 0x08; + sound2EnvelopeATLReload = sound2EnvelopeATL = 689 * (data & 7); + break; + case NR23: + freq = (((int)(gbMemory[NR24] & 7)) << 8) | data; + sound2ATL = 172 * (64 - (gbMemory[NR21] & 0x3f)); + freq = 2048 - freq; + if(freq) + { + sound2Skip = SOUND_MAGIC / freq; + } + else + sound2Skip = 0; + break; + case NR24: + freq = (((int)(data&7) << 8) | gbMemory[NR23]); + freq = 2048 - freq; + sound2ATL = 172 * (64 - (gbMemory[NR21] & 0x3f)); + sound2Continue = data & 0x40; + if(freq) + { + sound2Skip = SOUND_MAGIC / freq; + } + else + sound2Skip = 0; + if(data & 0x80) + { + gbMemory[NR52] |= 2; + sound2EnvelopeVolume = gbMemory[NR22] >> 4; + sound2EnvelopeUpDown = gbMemory[NR22] & 0x08; + sound2ATL = 172 * (64 - (gbMemory[NR21] & 0x3f)); + sound2EnvelopeATLReload = sound2EnvelopeATL = 689 * (gbMemory[NR22] & 7); + + sound2Index = 0; + sound2On = 1; + } + break; + case NR30: + if(!(data & 0x80)) + { + gbMemory[NR52] &= 0xfb; + sound3On = 0; + } + break; + case NR31: + sound3ATL = 172 * (256-data); + break; + case NR32: + sound3OutputLevel = (data >> 5) & 3; + break; + case NR33: + freq = 2048 - (((int)(gbMemory[NR34]&7) << 8) | data); + if(freq) + { + sound3Skip = SOUND_MAGIC_2 / freq; + } + else + sound3Skip = 0; + break; + case NR34: + freq = 2048 - (((data &7) << 8) | (int)gbMemory[NR33]); + if(freq) + { + sound3Skip = SOUND_MAGIC_2 / freq; + } + else + { + sound3Skip = 0; + } + sound3Continue = data & 0x40; + if((data & 0x80) && (gbMemory[NR30] & 0x80)) + { + gbMemory[NR52] |= 4; + sound3ATL = 172 * (256 - gbMemory[NR31]); + sound3Index = 0; + sound3On = 1; + } + break; + case NR41: + sound4ATL = 172 * (64 - (data & 0x3f)); + break; + case NR42: + sound4EnvelopeVolume = data >> 4; + sound4EnvelopeUpDown = data & 0x08; + sound4EnvelopeATLReload = sound4EnvelopeATL = 689 * (data & 7); + break; + case NR43: + freq = soundFreqRatio[data & 7]; + sound4NSteps = data & 0x08; + + sound4Skip = (freq << 8) / NOISE_MAGIC; + + sound4Clock = data >> 4; + + freq = freq / soundShiftClock[sound4Clock]; + + sound4ShiftSkip = (freq << 8) / NOISE_MAGIC; + + break; + case NR44: + sound4Continue = data & 0x40; + if(data & 0x80) + { + gbMemory[NR52] |= 8; + sound4EnvelopeVolume = gbMemory[NR42] >> 4; + sound4EnvelopeUpDown = gbMemory[NR42] & 0x08; + sound4ATL = 172 * (64 - (gbMemory[NR41] & 0x3f)); + sound4EnvelopeATLReload = sound4EnvelopeATL = 689 * (gbMemory[NR42] & 7); + + sound4On = 1; + + sound4Index = 0; + sound4ShiftIndex = 0; + + freq = soundFreqRatio[gbMemory[NR43] & 7]; + + sound4Skip = (freq << 8) / NOISE_MAGIC; + + sound4NSteps = gbMemory[NR43] & 0x08; + + freq = freq / soundShiftClock[gbMemory[NR43] >> 4]; + + sound4ShiftSkip = (freq << 8) / NOISE_MAGIC; + if(sound4NSteps) + sound4ShiftRight = 0x7fff; + else + sound4ShiftRight = 0x7f; + } + break; + case NR50: + soundVIN = data & 0x88; + soundLevel1 = data & 7; + soundLevel2 = (data >> 4) & 7; + break; + case NR51: + soundBalance = (data & soundEnableFlag); + break; + case NR52: + soundMasterOn = data & 0x80; + if(!(data & 0x80)) + { + sound1On = 0; + sound2On = 0; + sound3On = 0; + sound4On = 0; + } + break; + } + + gbDigitalSound = true; + + if(sound1On && sound1EnvelopeVolume != 0) + gbDigitalSound = false; + if(sound2On && sound2EnvelopeVolume != 0) + gbDigitalSound = false; + if(sound3On && sound3OutputLevel != 0) + gbDigitalSound = false; + if(sound4On && sound4EnvelopeVolume != 0) + gbDigitalSound = false; +} + +void gbSoundChannel1() +{ + int vol = sound1EnvelopeVolume; + + int freq = 0; + + int value = 0; + + if(sound1On && (sound1ATL || !sound1Continue)) + { + sound1Index += soundQuality*sound1Skip; + sound1Index &= 0x1fffffff; + + value = ((s8)sound1Wave[sound1Index>>24]) * vol; + } + + soundBuffer[0][soundIndex] = value; + + + if(sound1On) + { + if(sound1ATL) + { + sound1ATL-=soundQuality; + + if(sound1ATL <=0 && sound1Continue) + { + gbMemory[NR52] &= 0xfe; + sound1On = 0; + } + } + + if(sound1EnvelopeATL) + { + sound1EnvelopeATL-=soundQuality; + + if(sound1EnvelopeATL<=0) + { + if(sound1EnvelopeUpDown) + { + if(sound1EnvelopeVolume < 15) + sound1EnvelopeVolume++; + } + else + { + if(sound1EnvelopeVolume) + sound1EnvelopeVolume--; + } + + sound1EnvelopeATL += sound1EnvelopeATLReload; + } + } + + if(sound1SweepATL) + { + sound1SweepATL-=soundQuality; + + if(sound1SweepATL<=0) + { + freq = (((int)(gbMemory[NR14]&7) << 8) | gbMemory[NR13]); + + int updown = 1; + + if(sound1SweepUpDown) + updown = -1; + + int newfreq = 0; + if(sound1SweepSteps) + { + newfreq = freq + updown * freq / (1 << sound1SweepSteps); + if(newfreq == freq) + newfreq = 0; + } + else + newfreq = freq; + + if(newfreq < 0) + { + sound1SweepATL += sound1SweepATLReload; + } + else if(newfreq > 2047) + { + sound1SweepATL = 0; + sound1On = 0; + gbMemory[NR52] &= 0xfe; + } + else + { + sound1SweepATL += sound1SweepATLReload; + sound1Skip = SOUND_MAGIC/(2048 - newfreq); + + gbMemory[NR13] = newfreq & 0xff; + gbMemory[NR14] = (gbMemory[NR14] & 0xf8) |((newfreq >> 8) & 7); + } + } + } + } +} + +void gbSoundChannel2() +{ + // int freq = 0; + int vol = sound2EnvelopeVolume; + + int value = 0; + + if(sound2On && (sound2ATL || !sound2Continue)) + { + sound2Index += soundQuality*sound2Skip; + sound2Index &= 0x1fffffff; + + value = ((s8)sound2Wave[sound2Index>>24]) * vol; + } + + soundBuffer[1][soundIndex] = value; + + if(sound2On) + { + if(sound2ATL) + { + sound2ATL-=soundQuality; + + if(sound2ATL <= 0 && sound2Continue) + { + gbMemory[NR52] &= 0xfd; + sound2On = 0; + } + } + + if(sound2EnvelopeATL) + { + sound2EnvelopeATL-=soundQuality; + + if(sound2EnvelopeATL <= 0) + { + if(sound2EnvelopeUpDown) + { + if(sound2EnvelopeVolume < 15) + sound2EnvelopeVolume++; + } + else + { + if(sound2EnvelopeVolume) + sound2EnvelopeVolume--; + } + sound2EnvelopeATL += sound2EnvelopeATLReload; + } + } + } +} + +void gbSoundChannel3() +{ + int value = sound3Last; + + if(sound3On && (sound3ATL || !sound3Continue)) + { + sound3Index += soundQuality*sound3Skip; + sound3Index &= 0x1fffffff; + + value = gbMemory[0xff30 + (sound3Index>>25)]; + + if( (sound3Index & 0x01000000)) + { + value &= 0x0f; + } + else + { + value >>= 4; + } + + value -= 8; + + switch(sound3OutputLevel) + { + case 0: + value = 0; + break; + case 1: + break; + case 2: + value = (value >> 1); + break; + case 3: + value = (value >> 2); + break; + } + sound3Last = value; + } + + soundBuffer[2][soundIndex] = value; + + if(sound3On) + { + if(sound3ATL) + { + sound3ATL-=soundQuality; + + if(sound3ATL <= 0 && sound3Continue) + { + gbMemory[NR52] &= 0xfb; + sound3On = 0; + } + } + } +} + +void gbSoundChannel4() +{ + int vol = sound4EnvelopeVolume; + + int value = 0; + + if(sound4Clock <= 0x0c) + { + if(sound4On && (sound4ATL || !sound4Continue)) + { + sound4Index += soundQuality*sound4Skip; + sound4ShiftIndex += soundQuality*sound4ShiftSkip; + + if(sound4NSteps) + { + while(sound4ShiftIndex > 0x1fffff) + { + sound4ShiftRight = (((sound4ShiftRight << 6) ^ + (sound4ShiftRight << 5)) & 0x40) | + (sound4ShiftRight >> 1); + sound4ShiftIndex -= 0x200000; + } + } + else + { + while(sound4ShiftIndex > 0x1fffff) + { + sound4ShiftRight = (((sound4ShiftRight << 14) ^ + (sound4ShiftRight << 13)) & 0x4000) | + (sound4ShiftRight >> 1); + + sound4ShiftIndex -= 0x200000; + } + } + + sound4Index &= 0x1fffff; + sound4ShiftIndex &= 0x1fffff; + + value = ((sound4ShiftRight & 1)*2-1) * vol; + } + else + { + value = 0; + } + } + + soundBuffer[3][soundIndex] = value; + + if(sound4On) + { + if(sound4ATL) + { + sound4ATL-=soundQuality; + + if(sound4ATL <= 0 && sound4Continue) + { + gbMemory[NR52] &= 0xfd; + sound4On = 0; + } + } + + if(sound4EnvelopeATL) + { + sound4EnvelopeATL-=soundQuality; + + if(sound4EnvelopeATL <= 0) + { + if(sound4EnvelopeUpDown) + { + if(sound4EnvelopeVolume < 15) + sound4EnvelopeVolume++; + } + else + { + if(sound4EnvelopeVolume) + sound4EnvelopeVolume--; + } + sound4EnvelopeATL += sound4EnvelopeATLReload; + } + } + } +} + +void gbSoundMix() +{ + int res = 0; + + if(soundBalance & 16) + { + res += ((s8)soundBuffer[0][soundIndex]); + } + if(soundBalance & 32) + { + res += ((s8)soundBuffer[1][soundIndex]); + } + if(soundBalance & 64) + { + res += ((s8)soundBuffer[2][soundIndex]); + } + if(soundBalance & 128) + { + res += ((s8)soundBuffer[3][soundIndex]); + } + + if(gbDigitalSound) + res = soundLevel1*256; + else + res *= soundLevel1*60; + + if(soundEcho) + { + res *= 2; + res += soundFilter[soundEchoIndex]; + res /= 2; + soundFilter[soundEchoIndex++] = res; + } + + if(soundLowPass) + { + soundLeft[4] = soundLeft[3]; + soundLeft[3] = soundLeft[2]; + soundLeft[2] = soundLeft[1]; + soundLeft[1] = soundLeft[0]; + soundLeft[0] = res; + res = (soundLeft[4] + 2*soundLeft[3] + 8*soundLeft[2] + 2*soundLeft[1] + + soundLeft[0])/14; + } + + switch(soundVolume) + { + case 0: + case 1: + case 2: + case 3: + res *= (soundVolume+1); + break; + case 4: + res >>= 2; + break; + case 5: + res >>= 1; + break; + } + + if(res > 32767) + res = 32767; + if(res < -32768) + res = -32768; + + if(soundReverse) + soundFinalWave[++soundBufferIndex] = res; + else + soundFinalWave[soundBufferIndex++] = res; + + res = 0; + + if(soundBalance & 1) + { + res += ((s8)soundBuffer[0][soundIndex]); + } + if(soundBalance & 2) + { + res += ((s8)soundBuffer[1][soundIndex]); + } + if(soundBalance & 4) + { + res += ((s8)soundBuffer[2][soundIndex]); + } + if(soundBalance & 8) + { + res += ((s8)soundBuffer[3][soundIndex]); + } + + if(gbDigitalSound) + res = soundLevel2*256; + else + res *= soundLevel2*60; + + if(soundEcho) + { + res *= 2; + res += soundFilter[soundEchoIndex]; + res /= 2; + soundFilter[soundEchoIndex++] = res; + + if(soundEchoIndex >= 4000) + soundEchoIndex = 0; + } + + if(soundLowPass) + { + soundRight[4] = soundRight[3]; + soundRight[3] = soundRight[2]; + soundRight[2] = soundRight[1]; + soundRight[1] = soundRight[0]; + soundRight[0] = res; + res = (soundRight[4] + 2*soundRight[3] + 8*soundRight[2] + 2*soundRight[1] + + soundRight[0])/14; + } + + switch(soundVolume) + { + case 0: + case 1: + case 2: + case 3: + res *= (soundVolume+1); + break; + case 4: + res >>= 2; + break; + case 5: + res >>= 1; + break; + } + + if(res > 32767) + res = 32767; + if(res < -32768) + res = -32768; + + if(soundReverse) + soundFinalWave[-1+soundBufferIndex++] = res; + else + soundFinalWave[soundBufferIndex++] = res; +} + +void gbSoundTick() +{ + if(systemSoundOn) + { + if(soundMasterOn) + { + gbSoundChannel1(); + gbSoundChannel2(); + gbSoundChannel3(); + gbSoundChannel4(); + + gbSoundMix(); + } + else + { + soundFinalWave[soundBufferIndex++] = 0; + soundFinalWave[soundBufferIndex++] = 0; + } + + soundIndex++; + + if(2*soundBufferIndex >= soundBufferLen) + { + if(systemSoundOn) + { + if(soundPaused) + { + soundResume(); + } + + systemWriteDataToSoundBuffer(); + } + soundIndex = 0; + soundBufferIndex = 0; + } + } +} + +void gbSoundReset() +{ + soundPaused = 1; + soundPlay = 0; + SOUND_CLOCK_TICKS = soundQuality * 24; + soundTicks = SOUND_CLOCK_TICKS; + soundNextPosition = 0; + soundMasterOn = 1; + soundIndex = 0; + soundBufferIndex = 0; + soundLevel1 = 7; + soundLevel2 = 7; + soundVIN = 0; + + sound1On = 0; + sound1ATL = 0; + sound1Skip = 0; + sound1Index = 0; + sound1Continue = 0; + sound1EnvelopeVolume = 0; + sound1EnvelopeATL = 0; + sound1EnvelopeUpDown = 0; + sound1EnvelopeATLReload = 0; + sound1SweepATL = 0; + sound1SweepATLReload = 0; + sound1SweepSteps = 0; + sound1SweepUpDown = 0; + sound1SweepStep = 0; + sound1Wave = soundWavePattern[2]; + + sound2On = 0; + sound2ATL = 0; + sound2Skip = 0; + sound2Index = 0; + sound2Continue = 0; + sound2EnvelopeVolume = 0; + sound2EnvelopeATL = 0; + sound2EnvelopeUpDown = 0; + sound2EnvelopeATLReload = 0; + sound2Wave = soundWavePattern[2]; + + sound3On = 0; + sound3ATL = 0; + sound3Skip = 0; + sound3Index = 0; + sound3Continue = 0; + sound3OutputLevel = 0; + + sound4On = 0; + sound4Clock = 0; + sound4ATL = 0; + sound4Skip = 0; + sound4Index = 0; + sound4ShiftRight = 0x7f; + sound4NSteps = 0; + sound4CountDown = 0; + sound4Continue = 0; + sound4EnvelopeVolume = 0; + sound4EnvelopeATL = 0; + sound4EnvelopeUpDown = 0; + sound4EnvelopeATLReload = 0; + + // don't translate + if(soundDebug) + { + log("*** Sound Init ***\n"); + } + + gbSoundEvent(0xff10, 0x80); + gbSoundEvent(0xff11, 0xbf); + gbSoundEvent(0xff12, 0xf3); + gbSoundEvent(0xff14, 0xbf); + gbSoundEvent(0xff16, 0x3f); + gbSoundEvent(0xff17, 0x00); + gbSoundEvent(0xff19, 0xbf); + + gbSoundEvent(0xff1a, 0x7f); + gbSoundEvent(0xff1b, 0xff); + gbSoundEvent(0xff1c, 0xbf); + gbSoundEvent(0xff1e, 0xbf); + + gbSoundEvent(0xff20, 0xff); + gbSoundEvent(0xff21, 0x00); + gbSoundEvent(0xff22, 0x00); + gbSoundEvent(0xff23, 0xbf); + gbSoundEvent(0xff24, 0x77); + gbSoundEvent(0xff25, 0xf3); + + gbSoundEvent(0xff26, 0xf0); + + // don't translate + if(soundDebug) + { + log("*** Sound Init Complete ***\n"); + } + + sound1On = 0; + sound2On = 0; + sound3On = 0; + sound4On = 0; + + int addr = 0xff30; + + while(addr < 0xff40) + { + gbMemory[addr++] = 0x00; + gbMemory[addr++] = 0xff; + } + + memset(soundFinalWave, 0x00, soundBufferLen); + + + memset(soundFilter, 0, sizeof(soundFilter)); + soundEchoIndex = 0; +} + +extern bool soundInit(); +extern void soundShutdown(); + +void gbSoundSetQuality(int quality) +{ + if(soundQuality != quality && systemCanChangeSoundQuality()) + { + if(!soundOffFlag) + soundShutdown(); + soundQuality = quality; + soundNextPosition = 0; + if(!soundOffFlag) + soundInit(); + SOUND_CLOCK_TICKS = (gbSpeed ? 2 : 1) * 24 * soundQuality; + soundIndex = 0; + soundBufferIndex = 0; + } + else + { + soundNextPosition = 0; + SOUND_CLOCK_TICKS = (gbSpeed ? 2 : 1) * 24 * soundQuality; + soundIndex = 0; + soundBufferIndex = 0; + } +} + +variable_desc gbSoundSaveStruct[] = { + { &soundPaused, sizeof(int) }, + { &soundPlay, sizeof(int) }, + { &soundTicks, sizeof(int) }, + { &SOUND_CLOCK_TICKS, sizeof(int) }, + { &soundLevel1, sizeof(int) }, + { &soundLevel2, sizeof(int) }, + { &soundBalance, sizeof(int) }, + { &soundMasterOn, sizeof(int) }, + { &soundIndex, sizeof(int) }, + { &soundVIN, sizeof(int) }, + { &sound1On, sizeof(int) }, + { &sound1ATL, sizeof(int) }, + { &sound1Skip, sizeof(int) }, + { &sound1Index, sizeof(int) }, + { &sound1Continue, sizeof(int) }, + { &sound1EnvelopeVolume, sizeof(int) }, + { &sound1EnvelopeATL, sizeof(int) }, + { &sound1EnvelopeATLReload, sizeof(int) }, + { &sound1EnvelopeUpDown, sizeof(int) }, + { &sound1SweepATL, sizeof(int) }, + { &sound1SweepATLReload, sizeof(int) }, + { &sound1SweepSteps, sizeof(int) }, + { &sound1SweepUpDown, sizeof(int) }, + { &sound1SweepStep, sizeof(int) }, + { &sound2On, sizeof(int) }, + { &sound2ATL, sizeof(int) }, + { &sound2Skip, sizeof(int) }, + { &sound2Index, sizeof(int) }, + { &sound2Continue, sizeof(int) }, + { &sound2EnvelopeVolume, sizeof(int) }, + { &sound2EnvelopeATL, sizeof(int) }, + { &sound2EnvelopeATLReload, sizeof(int) }, + { &sound2EnvelopeUpDown, sizeof(int) }, + { &sound3On, sizeof(int) }, + { &sound3ATL, sizeof(int) }, + { &sound3Skip, sizeof(int) }, + { &sound3Index, sizeof(int) }, + { &sound3Continue, sizeof(int) }, + { &sound3OutputLevel, sizeof(int) }, + { &sound4On, sizeof(int) }, + { &sound4ATL, sizeof(int) }, + { &sound4Skip, sizeof(int) }, + { &sound4Index, sizeof(int) }, + { &sound4Clock, sizeof(int) }, + { &sound4ShiftRight, sizeof(int) }, + { &sound4ShiftSkip, sizeof(int) }, + { &sound4ShiftIndex, sizeof(int) }, + { &sound4NSteps, sizeof(int) }, + { &sound4CountDown, sizeof(int) }, + { &sound4Continue, sizeof(int) }, + { &sound4EnvelopeVolume, sizeof(int) }, + { &sound4EnvelopeATL, sizeof(int) }, + { &sound4EnvelopeATLReload, sizeof(int) }, + { &sound4EnvelopeUpDown, sizeof(int) }, + { &soundEnableFlag, sizeof(int) }, + { NULL, 0 } + }; + +void gbSoundSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, gbSoundSaveStruct); + + utilGzWrite(gzFile, soundBuffer, 4*735); + utilGzWrite(gzFile, soundFinalWave, 2*735); + utilGzWrite(gzFile, &soundQuality, sizeof(int)); +} + +void gbSoundReadGame(int version,gzFile gzFile) +{ + utilReadData(gzFile, gbSoundSaveStruct); + + soundBufferIndex = soundIndex * 2; + + utilGzRead(gzFile, soundBuffer, 4*735); + utilGzRead(gzFile, soundFinalWave, 2*735); + + if(version >=7) + { + int quality = 1; + utilGzRead(gzFile, &quality, sizeof(int)); + gbSoundSetQuality(quality); + } + else + { + soundQuality = -1; + gbSoundSetQuality(1); + } + + sound1Wave = soundWavePattern[gbMemory[NR11] >> 6]; + sound2Wave = soundWavePattern[gbMemory[NR21] >> 6]; +} diff --git a/src/gb/gbSound.h b/src/gb/gbSound.h new file mode 100644 index 0000000..50658b2 --- /dev/null +++ b/src/gb/gbSound.h @@ -0,0 +1,59 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#define NR10 0xff10 +#define NR11 0xff11 +#define NR12 0xff12 +#define NR13 0xff13 +#define NR14 0xff14 +#define NR21 0xff16 +#define NR22 0xff17 +#define NR23 0xff18 +#define NR24 0xff19 +#define NR30 0xff1a +#define NR31 0xff1b +#define NR32 0xff1c +#define NR33 0xff1d +#define NR34 0xff1e +#define NR41 0xff20 +#define NR42 0xff21 +#define NR43 0xff22 +#define NR44 0xff23 +#define NR50 0xff24 +#define NR51 0xff25 +#define NR52 0xff26 + +#define SOUND_EVENT(address,value) \ + gbSoundEvent(address,value) + +extern void gbSoundTick(); +extern void gbSoundPause(); +extern void gbSoundResume(); +extern void gbSoundEnable(int); +extern void gbSoundDisable(int); +extern int gbSoundGetEnable(); +extern void gbSoundReset(); +extern void gbSoundSaveGame(gzFile); +extern void gbSoundReadGame(int,gzFile); +extern void gbSoundEvent(register u16, register int); +extern void gbSoundSetQuality(int); + +extern int soundTicks; +extern int soundQuality; +extern int SOUND_CLOCK_TICKS; diff --git a/src/getopt.c b/src/getopt.c new file mode 100644 index 0000000..6da738c --- /dev/null +++ b/src/getopt.c @@ -0,0 +1,1061 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98 + Free Software Foundation, Inc. + + NOTE: This source is derived from an old version taken from the GNU C + Library (glibc). + + 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 2, or (at your option) any + later version. + + 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 program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +#ifdef _MSC_VER +#include +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +# if HAVE_STRING_H +# include +# else +# if HAVE_STRINGS_H +# include +# endif +# endif + +/* Avoid depending on library functions or files +whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) +const char *str; +int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. +If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. +That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, +and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) +char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) +int argc; +char *const *argv; +const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) +int argc; +char *const *argv; +const char *optstring; +const struct option *longopts; +int *longind; +int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ + %s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) +int argc; +char *const *argv; +const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) +int argc; +char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/getopt.h b/src/getopt.h new file mode 100644 index 0000000..1931abf --- /dev/null +++ b/src/getopt.h @@ -0,0 +1,142 @@ +/* Declarations for getopt. + Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 2000 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + 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 2, or (at your option) any + later version. + + 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 program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" + { +#endif + + /* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + + extern char *optarg; + + /* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + + extern int optind; + + /* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + + extern int opterr; + + /* Set to an option character which was unrecognized. */ + + extern int optopt; + + /* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + + struct option + { +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; + }; + + /* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ + /* HAVE_DECL_* is a three-state macro: undefined, 0 or 1. If it is + undefined, we haven't run the autoconf check so provide the + declaration without arguments. If it is 0, we checked and failed + to find the declaration so provide a fully prototyped one. If it + is 1, we found it so don't provide any declaration at all. */ +#if defined (__GNU_LIBRARY__) || (defined (HAVE_DECL_GETOPT) && !HAVE_DECL_GETOPT) + /* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ + extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +# if !defined (HAVE_DECL_GETOPT) + extern int getopt (); +# endif +#endif /* __GNU_LIBRARY__ */ + extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); + extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + + /* Internal only. Users should not call this directly. */ + extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ + extern int getopt (); + extern int getopt_long (); + extern int getopt_long_only (); + + extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus + } +#endif + +#endif /* getopt.h */ diff --git a/src/getopt1.c b/src/getopt1.c new file mode 100644 index 0000000..5302231 --- /dev/null +++ b/src/getopt1.c @@ -0,0 +1,191 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + + NOTE: This source is derived from an old version taken from the GNU C + Library (glibc). + + 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 2, or (at your option) any + later version. + + 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 program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) +int argc; +char *const *argv; +const char *options; +const struct option *long_options; +int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) +int argc; +char *const *argv; +const char *options; +const struct option *long_options; +int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) +int argc; +char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0 + }, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/interframe.cpp b/src/interframe.cpp new file mode 100644 index 0000000..44e688d --- /dev/null +++ b/src/interframe.cpp @@ -0,0 +1,643 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "System.h" +#include +#include + +#ifdef MMX +extern "C" bool cpu_mmx; +#endif + +/* + * Thanks to Kawaks' Mr. K for the code + + Incorporated into vba by Anthony Di Franco +*/ + +static u8 *frm1 = NULL; +static u8 *frm2 = NULL; +static u8 *frm3 = NULL; + +extern int RGB_LOW_BITS_MASK; +extern u32 qRGB_COLOR_MASK[2]; + +static void Init() +{ + frm1 = (u8 *)calloc(322*242,4); + // 1 frame ago + frm2 = (u8 *)calloc(322*242,4); + // 2 frames ago + frm3 = (u8 *)calloc(322*242,4); + // 3 frames ago +} + +void InterframeCleanup() +{ + if(frm1) + free(frm1); + if(frm2) + free(frm2); + if(frm3) + free(frm3); + frm1 = frm2 = frm3 = NULL; +} + +#ifdef MMX +static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = (u16 *)frm1; + u16 *src2 = (u16 *)frm2; + u16 *src3 = (u16 *)frm3; + + int count = width >> 2; + + for(int i = 0; i < height; i++) + { +#ifdef __GNUC__ + asm volatile ( + "push %4\n" + "movq 0(%5), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq 0(%2), %%mm2\n" // src2 + "movq 0(%3), %%mm3\n" // src3 + "movq %%mm0, 0(%3)\n" // src3 = src0 + "movq %%mm0, %%mm4\n" + "movq %%mm1, %%mm5\n" + "pcmpeqw %%mm2, %%mm5\n" // src1 == src2 (A) + "pcmpeqw %%mm3, %%mm4\n" // src3 == src0 (B) + "por %%mm5, %%mm4\n" // A | B + "movq %%mm2, %%mm5\n" + "pcmpeqw %%mm0, %%mm5\n" // src0 == src2 (C) + "pcmpeqw %%mm1, %%mm3\n" // src1 == src3 (D) + "por %%mm3, %%mm5\n" // C|D + "pandn %%mm5, %%mm4\n" // (!(A|B))&(C|D) + "movq %%mm0, %%mm2\n" + "pand %%mm7, %%mm2\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrlw $1, %%mm2\n" // (color & colorMask) >> 1 (E) + "psrlw $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddw %%mm2, %%mm1\n" // E+F + "pand %%mm4, %%mm1\n" // (E+F) & res + "pandn %%mm0, %%mm4\n" // color& !res + + "por %%mm1, %%mm4\n" + "movq %%mm4, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $8, %3\n" + + "decl %4\n" + "jnz 0b\n" + "pop %4\n" + "emms\n" + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (src3) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, src3; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq mm2, qword ptr [ecx]; // src2 + movq mm3, qword ptr [edx]; // src3 + movq qword ptr [edx], mm0; // src3 = src0 + movq mm4, mm0; + movq mm5, mm1; + pcmpeqw mm5, mm2; // src1 == src2 (A) + pcmpeqw mm4, mm3; // src3 == src0 (B) + por mm4, mm5; // A | B + movq mm5, mm2; + pcmpeqw mm5, mm0; // src0 == src2 (C) + pcmpeqw mm3, mm1; // src1 == src3 (D) + por mm5, mm3; // C|D + pandn mm4, mm5; // (!(A|B))&(C|D) + movq mm2, mm0; + pand mm2, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrlw mm2, 1; // (color & colorMask) >> 1 (E) + psrlw mm1, 1; // (src & colorMask) >> 1 (F) + paddw mm1, mm2; // E+F + pand mm1, mm4; // (E+F) & res + pandn mm4, mm0; // color & !res + + por mm4, mm1; + movq qword ptr [eax], mm4; // src0 = res + + add eax, 8; + add ebx, 8; + add ecx, 8; + add edx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov src3, edx; + emms; + } +#endif + src0+=2; + src1+=2; + src2+=2; + src3+=2; + } + + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} +#endif + +void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + if(frm1 == NULL) + { + Init(); + } +#ifdef MMX + if(cpu_mmx) + { + SmartIB_MMX(srcPtr, srcPitch, width, height); + return; + } +#endif + + u16 colorMask = ~RGB_LOW_BITS_MASK; + + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = (u16 *)frm1; + u16 *src2 = (u16 *)frm2; + u16 *src3 = (u16 *)frm3; + + int sPitch = srcPitch >> 1; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) + { + u16 color = src0[pos]; + src0[pos] = + (src1[pos] != src2[pos]) && + (src3[pos] != color) && + ((color == src2[pos]) || (src1[pos] == src3[pos])) + ? (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)) : + color; + src3[pos] = color; /* oldest buffer now holds newest frame */ + pos++; + } + + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} + +#ifdef MMX +static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = (u32 *)frm1; + u32 *src2 = (u32 *)frm2; + u32 *src3 = (u32 *)frm3; + + int count = width >> 1; + + for(int i = 0; i < height; i++) + { +#ifdef __GNUC__ + asm volatile ( + "push %4\n" + "movq 0(%5), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq 0(%2), %%mm2\n" // src2 + "movq 0(%3), %%mm3\n" // src3 + "movq %%mm0, 0(%3)\n" // src3 = src0 + "movq %%mm0, %%mm4\n" + "movq %%mm1, %%mm5\n" + "pcmpeqd %%mm2, %%mm5\n" // src1 == src2 (A) + "pcmpeqd %%mm3, %%mm4\n" // src3 == src0 (B) + "por %%mm5, %%mm4\n" // A | B + "movq %%mm2, %%mm5\n" + "pcmpeqd %%mm0, %%mm5\n" // src0 == src2 (C) + "pcmpeqd %%mm1, %%mm3\n" // src1 == src3 (D) + "por %%mm3, %%mm5\n" // C|D + "pandn %%mm5, %%mm4\n" // (!(A|B))&(C|D) + "movq %%mm0, %%mm2\n" + "pand %%mm7, %%mm2\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrld $1, %%mm2\n" // (color & colorMask) >> 1 (E) + "psrld $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddd %%mm2, %%mm1\n" // E+F + "pand %%mm4, %%mm1\n" // (E+F) & res + "pandn %%mm0, %%mm4\n" // color& !res + + "por %%mm1, %%mm4\n" + "movq %%mm4, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $8, %3\n" + + "decl %4\n" + "jnz 0b\n" + "pop %4\n" + "emms\n" + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (src3) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov ecx, src2; + mov edx, src3; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq mm2, qword ptr [ecx]; // src2 + movq mm3, qword ptr [edx]; // src3 + movq qword ptr [edx], mm0; // src3 = src0 + movq mm4, mm0; + movq mm5, mm1; + pcmpeqd mm5, mm2; // src1 == src2 (A) + pcmpeqd mm4, mm3; // src3 == src0 (B) + por mm4, mm5; // A | B + movq mm5, mm2; + pcmpeqd mm5, mm0; // src0 == src2 (C) + pcmpeqd mm3, mm1; // src1 == src3 (D) + por mm5, mm3; // C|D + pandn mm4, mm5; // (!(A|B))&(C|D) + movq mm2, mm0; + pand mm2, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrld mm2, 1; // (color & colorMask) >> 1 (E) + psrld mm1, 1; // (src & colorMask) >> 1 (F) + paddd mm1, mm2; // E+F + pand mm1, mm4; // (E+F) & res + pandn mm4, mm0; // color & !res + + por mm4, mm1; + movq qword ptr [eax], mm4; // src0 = res + + add eax, 8; + add ebx, 8; + add ecx, 8; + add edx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + mov src2, ecx; + mov src3, edx; + emms; + } +#endif + + src0++; + src1++; + src2++; + src3++; + } + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} +#endif + +void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + if(frm1 == NULL) + { + Init(); + } +#ifdef MMX + if(cpu_mmx) + { + SmartIB32_MMX(srcPtr, srcPitch, width, height); + return; + } +#endif + + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = (u32 *)frm1; + u32 *src2 = (u32 *)frm2; + u32 *src3 = (u32 *)frm3; + + u32 colorMask = 0xfefefe; + + int sPitch = srcPitch >> 2; + int pos = 0; + + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) + { + u32 color = src0[pos]; + src0[pos] = + (src1[pos] != src2[pos]) && + (src3[pos] != color) && + ((color == src2[pos]) || (src1[pos] == src3[pos])) + ? (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)) : + color; + src3[pos] = color; /* oldest buffer now holds newest frame */ + pos++; + } + + /* Swap buffers around */ + u8 *temp = frm1; + frm1 = frm3; + frm3 = frm2; + frm2 = temp; +} + +#ifdef MMX +static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = (u16 *)frm1; + + int count = width >> 2; + + for(int i = 0; i < height; i++) + { +#ifdef __GNUC__ + asm volatile ( + "push %2\n" + "movq 0(%3), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq %%mm0, 0(%1)\n" // src1 = src0 + "pand %%mm7, %%mm0\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrlw $1, %%mm0\n" // (color & colorMask) >> 1 (E) + "psrlw $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddw %%mm1, %%mm0\n" // E+F + + "movq %%mm0, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + + "decl %2\n" + "jnz 0b\n" + "pop %2\n" + "emms\n" + : "+r" (src0), "+r" (src1) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq qword ptr [ebx], mm0; // src1 = src0 + pand mm0, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrlw mm0, 1; // (color & colorMask) >> 1 (E) + psrlw mm1, 1; // (src & colorMask) >> 1 (F) + paddw mm0, mm1; // E+F + + movq qword ptr [eax], mm0; // src0 = res + + add eax, 8; + add ebx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + emms; + } +#endif + src0+=2; + src1+=2; + } +} +#endif + +void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + if(frm1 == NULL) + { + Init(); + } + +#ifdef MMX + if(cpu_mmx) + { + MotionBlurIB_MMX(srcPtr, srcPitch, width, height); + return; + } +#endif + + u16 colorMask = ~RGB_LOW_BITS_MASK; + + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = (u16 *)frm1; + + int sPitch = srcPitch >> 1; + + int pos = 0; + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) + { + u16 color = src0[pos]; + src0[pos] = + (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)); + src1[pos] = color; + pos++; + } +} + +#ifdef MMX +static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = (u32 *)frm1; + + int count = width >> 1; + + for(int i = 0; i < height; i++) + { +#ifdef __GNUC__ + asm volatile ( + "push %2\n" + "movq 0(%3), %%mm7\n" // colorMask + "0:\n" + "movq 0(%0), %%mm0\n" // src0 + "movq 0(%1), %%mm1\n" // src1 + "movq %%mm0, 0(%1)\n" // src1 = src0 + "pand %%mm7, %%mm0\n" // color & colorMask + "pand %%mm7, %%mm1\n" // src1 & colorMask + "psrld $1, %%mm0\n" // (color & colorMask) >> 1 (E) + "psrld $1, %%mm1\n" // (src & colorMask) >> 1 (F) + "paddd %%mm1, %%mm0\n" // E+F + + "movq %%mm0, 0(%0)\n" // src0 = res + + "addl $8, %0\n" + "addl $8, %1\n" + + "decl %2\n" + "jnz 0b\n" + "pop %2\n" + "emms\n" + : "+r" (src0), "+r" (src1) + : "r" (count), "r" (qRGB_COLOR_MASK) + ); +#else + __asm { + movq mm7, qword ptr [qRGB_COLOR_MASK]; + mov eax, src0; + mov ebx, src1; + mov edi, count; + label0: + movq mm0, qword ptr [eax]; // src0 + movq mm1, qword ptr [ebx]; // src1 + movq qword ptr [ebx], mm0; // src1 = src0 + pand mm0, mm7; // color & colorMask + pand mm1, mm7; // src1 & colorMask + psrld mm0, 1; // (color & colorMask) >> 1 (E) + psrld mm1, 1; // (src & colorMask) >> 1 (F) + paddd mm0, mm1; // E+F + + movq qword ptr [eax], mm0; // src0 = res + + add eax, 8; + add ebx, 8; + + dec edi; + jnz label0; + mov src0, eax; + mov src1, ebx; + emms; + } +#endif + src0++; + src1++; + } +} +#endif + +void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + if(frm1 == NULL) + { + Init(); + } + +#ifdef MMX + if(cpu_mmx) + { + MotionBlurIB32_MMX(srcPtr, srcPitch, width, height); + return; + } +#endif + + u32 *src0 = (u32 *)srcPtr; + u32 *src1 = (u32 *)frm1; + + u32 colorMask = 0xfefefe; + + int sPitch = srcPitch >> 2; + int pos = 0; + + for (int j = 0; j < height; j++) + for (int i = 0; i < sPitch; i++) + { + u32 color = src0[pos]; + src0[pos] = (((color & colorMask) >> 1) + + ((src1[pos] & colorMask) >> 1)); + src1[pos] = color; + pos++; + } +} + +static int count = 0; + +void InterlaceIB(u8 *srcPtr, u32 srcPitch, int width, int height) +{ + if(frm1 == NULL) + { + Init(); + } + + u16 colorMask = ~RGB_LOW_BITS_MASK; + + u16 *src0 = (u16 *)srcPtr; + u16 *src1 = (u16 *)frm1; + + int sPitch = srcPitch >> 1; + + int pos = 0; + for (int j = 0; j < height; j++) + { + bool render = count ? (j & 1) != 0 : (j & 1) == 0; + if(render) + { + for (int i = 0; i < sPitch; i++) + { + u16 color = src0[pos]; + src0[pos] = + (((color & colorMask) >> 1) + ((((src1[pos] & colorMask) >> 1) & colorMask) >> 1)); + src1[pos] = color; + pos++; + } + } + else + { + for (int i = 0; i < sPitch; i++) + { + u16 color = src0[pos]; + src0[pos] = + (((((color & colorMask) >> 1) & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)); + src1[pos] = color; + pos++; + } + } + } + count = count ^ 1; +} diff --git a/src/interp.h b/src/interp.h new file mode 100644 index 0000000..569afe3 --- /dev/null +++ b/src/interp.h @@ -0,0 +1,298 @@ +/* + * This file is part of the Advance project. + * + * Copyright (C) 2003 Andrea Mazzoleni + * + * 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 2 of the License, or + * (at your option) any later version. + * + * 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 program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * In addition, as a special exception, Andrea Mazzoleni + * gives permission to link the code of this program with + * the MAME library (or with modified versions of MAME that use the + * same license as MAME), and distribute linked combinations including + * the two. You must obey the GNU General Public License in all + * respects for all of the code used other than MAME. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#ifndef __INTERP_H +#define __INTERP_H + +/***************************************************************************/ +/* Basic types */ + +/***************************************************************************/ +/* interpolation */ + +static unsigned interp_mask[2]; +static unsigned interp_bits_per_pixel; + +#define INTERP_16_MASK_1(v) (v & interp_mask[0]) +#define INTERP_16_MASK_2(v) (v & interp_mask[1]) + +static inline u16 interp_16_521(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*5 + INTERP_16_MASK_1(p2)*2 + INTERP_16_MASK_1(p3)*1) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*5 + INTERP_16_MASK_2(p2)*2 + INTERP_16_MASK_2(p3)*1) / 8); +} + +static inline u16 interp_16_332(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*3 + INTERP_16_MASK_1(p2)*3 + INTERP_16_MASK_1(p3)*2) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*3 + INTERP_16_MASK_2(p2)*3 + INTERP_16_MASK_2(p3)*2) / 8); +} + +static inline u16 interp_16_611(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*6 + INTERP_16_MASK_1(p2) + INTERP_16_MASK_1(p3)) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*6 + INTERP_16_MASK_2(p2) + INTERP_16_MASK_2(p3)) / 8); +} + +static inline u16 interp_16_71(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*7 + INTERP_16_MASK_1(p2)) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*7 + INTERP_16_MASK_2(p2)) / 8); +} + +static inline u16 interp_16_211(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*2 + INTERP_16_MASK_1(p2) + INTERP_16_MASK_1(p3)) / 4) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*2 + INTERP_16_MASK_2(p2) + INTERP_16_MASK_2(p3)) / 4); +} + +static inline u16 interp_16_772(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1(((INTERP_16_MASK_1(p1) + INTERP_16_MASK_1(p2))*7 + INTERP_16_MASK_1(p3)*2) / 16) + | INTERP_16_MASK_2(((INTERP_16_MASK_2(p1) + INTERP_16_MASK_2(p2))*7 + INTERP_16_MASK_2(p3)*2) / 16); +} + +static inline u16 interp_16_11(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1) + INTERP_16_MASK_1(p2)) / 2) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1) + INTERP_16_MASK_2(p2)) / 2); +} + +static inline u16 interp_16_31(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*3 + INTERP_16_MASK_1(p2)) / 4) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*3 + INTERP_16_MASK_2(p2)) / 4); +} + +static inline u16 interp_16_1411(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*14 + INTERP_16_MASK_1(p2) + INTERP_16_MASK_1(p3)) / 16) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*14 + INTERP_16_MASK_2(p2) + INTERP_16_MASK_2(p3)) / 16); +} + +static inline u16 interp_16_431(u16 p1, u16 p2, u16 p3) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*4 + INTERP_16_MASK_1(p2)*3 + INTERP_16_MASK_1(p3)) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*4 + INTERP_16_MASK_2(p2)*3 + INTERP_16_MASK_2(p3)) / 8); +} + +static inline u16 interp_16_53(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*5 + INTERP_16_MASK_1(p2)*3) / 8) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*5 + INTERP_16_MASK_2(p2)*3) / 8); +} + +static inline u16 interp_16_151(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*15 + INTERP_16_MASK_1(p2)) / 16) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*15 + INTERP_16_MASK_2(p2)) / 16); +} + +static inline u16 interp_16_97(u16 p1, u16 p2) +{ + return INTERP_16_MASK_1((INTERP_16_MASK_1(p1)*9 + INTERP_16_MASK_1(p2)*7) / 16) + | INTERP_16_MASK_2((INTERP_16_MASK_2(p1)*9 + INTERP_16_MASK_2(p2)*7) / 16); +} + +#define INTERP_32_MASK_1(v) (v & 0xFF00FF) +#define INTERP_32_MASK_2(v) (v & 0x00FF00) + +static inline u32 interp_32_521(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*5 + INTERP_32_MASK_1(p2)*2 + INTERP_32_MASK_1(p3)*1) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*5 + INTERP_32_MASK_2(p2)*2 + INTERP_32_MASK_2(p3)*1) / 8); +} + +static inline u32 interp_32_332(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*3 + INTERP_32_MASK_1(p2)*3 + INTERP_32_MASK_1(p3)*2) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*3 + INTERP_32_MASK_2(p2)*3 + INTERP_32_MASK_2(p3)*2) / 8); +} + +static inline u32 interp_32_211(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*2 + INTERP_32_MASK_1(p2) + INTERP_32_MASK_1(p3)) / 4) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*2 + INTERP_32_MASK_2(p2) + INTERP_32_MASK_2(p3)) / 4); +} + +static inline u32 interp_32_611(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*6 + INTERP_32_MASK_1(p2) + INTERP_32_MASK_1(p3)) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*6 + INTERP_32_MASK_2(p2) + INTERP_32_MASK_2(p3)) / 8); +} + +static inline u32 interp_32_71(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*7 + INTERP_32_MASK_1(p2)) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*7 + INTERP_32_MASK_2(p2)) / 8); +} + +static inline u32 interp_32_772(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1(((INTERP_32_MASK_1(p1) + INTERP_32_MASK_1(p2))*7 + INTERP_32_MASK_1(p3)*2) / 16) + | INTERP_32_MASK_2(((INTERP_32_MASK_2(p1) + INTERP_32_MASK_2(p2))*7 + INTERP_32_MASK_2(p3)*2) / 16); +} + +static inline u32 interp_32_11(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1) + INTERP_32_MASK_1(p2)) / 2) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1) + INTERP_32_MASK_2(p2)) / 2); +} + +static inline u32 interp_32_31(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*3 + INTERP_32_MASK_1(p2)) / 4) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*3 + INTERP_32_MASK_2(p2)) / 4); +} + +static inline u32 interp_32_1411(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*14 + INTERP_32_MASK_1(p2) + INTERP_32_MASK_1(p3)) / 16) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*14 + INTERP_32_MASK_2(p2) + INTERP_32_MASK_2(p3)) / 16); +} + +static inline u32 interp_32_431(u32 p1, u32 p2, u32 p3) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*4 + INTERP_32_MASK_1(p2)*3 + INTERP_32_MASK_1(p3)) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*4 + INTERP_32_MASK_2(p2)*3 + INTERP_32_MASK_2(p3)) / 8); +} + +static inline u32 interp_32_53(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*5 + INTERP_32_MASK_1(p2)*3) / 8) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*5 + INTERP_32_MASK_2(p2)*3) / 8); +} + +static inline u32 interp_32_151(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*15 + INTERP_32_MASK_1(p2)) / 16) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*15 + INTERP_32_MASK_2(p2)) / 16); +} + +static inline u32 interp_32_97(u32 p1, u32 p2) +{ + return INTERP_32_MASK_1((INTERP_32_MASK_1(p1)*9 + INTERP_32_MASK_1(p2)*7) / 16) + | INTERP_32_MASK_2((INTERP_32_MASK_2(p1)*9 + INTERP_32_MASK_2(p2)*7) / 16); +} + +/***************************************************************************/ +/* diff */ + +#define INTERP_Y_LIMIT (0x30*4) +#define INTERP_U_LIMIT (0x07*4) +#define INTERP_V_LIMIT (0x06*8) + +static int interp_16_diff(u16 p1, u16 p2) +{ + int r, g, b; + int y, u, v; + + if (p1 == p2) + return 0; + + if (interp_bits_per_pixel == 16) + { + b = (int)((p1 & 0x1F) - (p2 & 0x1F)) << 3; + g = (int)((p1 & 0x7E0) - (p2 & 0x7E0)) >> 3; + r = (int)((p1 & 0xF800) - (p2 & 0xF800)) >> 8; + } + else + { + b = (int)((p1 & 0x1F) - (p2 & 0x1F)) << 3; + g = (int)((p1 & 0x3E0) - (p2 & 0x3E0)) >> 2; + r = (int)((p1 & 0x7C00) - (p2 & 0x7C00)) >> 7; + } + + y = r + g + b; + u = r - b; + v = -r + 2*g - b; + + if (y < -INTERP_Y_LIMIT || y > INTERP_Y_LIMIT) + return 1; + + if (u < -INTERP_U_LIMIT || u > INTERP_U_LIMIT) + return 1; + + if (v < -INTERP_V_LIMIT || v > INTERP_V_LIMIT) + return 1; + + return 0; +} + +static int interp_32_diff(u32 p1, u32 p2) +{ + int r, g, b; + int y, u, v; + + if ((p1 & 0xF8F8F8) == (p2 & 0xF8F8F8)) + return 0; + + b = (int)((p1 & 0xFF) - (p2 & 0xFF)); + g = (int)((p1 & 0xFF00) - (p2 & 0xFF00)) >> 8; + r = (int)((p1 & 0xFF0000) - (p2 & 0xFF0000)) >> 16; + + y = r + g + b; + u = r - b; + v = -r + 2*g - b; + + if (y < -INTERP_Y_LIMIT || y > INTERP_Y_LIMIT) + return 1; + + if (u < -INTERP_U_LIMIT || u > INTERP_U_LIMIT) + return 1; + + if (v < -INTERP_V_LIMIT || v > INTERP_V_LIMIT) + return 1; + + return 0; +} + +static void interp_set(unsigned bits_per_pixel) +{ + interp_bits_per_pixel = bits_per_pixel; + + switch (bits_per_pixel) + { + case 15 : + interp_mask[0] = 0x7C1F; + interp_mask[1] = 0x03E0; + break; + case 16 : + interp_mask[0] = 0xF81F; + interp_mask[1] = 0x07E0; + break; + case 32 : + interp_mask[0] = 0xFF00FF; + interp_mask[1] = 0x00FF00; + break; + } +} + +#endif diff --git a/src/memgzio.c b/src/memgzio.c new file mode 100644 index 0000000..eef6b2c --- /dev/null +++ b/src/memgzio.c @@ -0,0 +1,750 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* memgzio.c - IO on .gz files in memory + * Adapted from original gzio.c from zlib library by Forgotten + */ + +/* @(#) $Id: memgzio.c,v 1.3 2004/01/17 23:07:32 kxu Exp $ */ + +#include +#include +#include + +#include "memgzio.h" + +/*struct internal_state {int dummy;};*/ /* for buggy compilers */ + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct _MemFile + { + char *memory; + char *next; + int available; + int error; + char mode; + } +MEMFILE; + +typedef struct mem_stream + { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + MEMFILE *file; /* memoru file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + long startpos; /* start of compressed data in file (header skipped) */ + } +mem_stream; + + +local gzFile gz_open OF((char *memory, const int available, const char *mode)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((mem_stream *s)); +local void check_header OF((mem_stream *s)); +local int destroy OF((mem_stream *s)); +local void putLong OF((MEMFILE *file, uLong x)); +local uLong getLong OF((mem_stream *s)); + +local MEMFILE *memOpen(char *memory, int available, char mode) +{ + MEMFILE *f; + + if(available <= 8) + return NULL; + + if(mode != 'w' && mode != 'r') + return NULL; + + f = (MEMFILE *)malloc(sizeof(MEMFILE)); + + f->memory = memory; + f->mode = mode; + f->error = 0; + + if(mode == 'w') + { + f->available = available - 8; + f->next = memory + 8; + memory[0] = 'V'; + memory[1] = 'B'; + memory[2] = 'A'; + memory[3] = ' '; + *((int *)(memory+4)) = 0; + } + else + { + if(memory[0] != 'V' || memory[1] != 'B' || memory[2] != 'A' || + memory[3] != ' ') + { + free(f); + return NULL; + } + f->available = *((int *)(memory+4)); + f->next = memory+8; + } + + return f; +} + +local size_t memWrite(const void *buffer, size_t size, size_t count, + MEMFILE *file) +{ + size_t total = size*count; + + if(file->mode != 'w') + { + file->error = 1; + return 0; + } + + if(total > (size_t)file->available) + { + total = file->available; + } + memcpy(file->next, buffer, total); + file->available -= total; + file->next += total; + return total; +} + +local size_t memRead(void *buffer, size_t size, size_t count, + MEMFILE *file) +{ + size_t total = size*count; + + if(file->mode != 'r') + { + file->error = 1; + return 0; + } + + if(file->available == 0) + return -1; + + if(total > (size_t)file->available) + { + total = file->available; + } + memcpy(buffer, file->next, total); + file->available -= total; + file->next += total; + return total; +} + +local int memPutc(int c, MEMFILE *file) +{ + if(file->mode != 'w') + { + file->error = 1; + return -1; + } + + if(file->available >= 1) + { + *file->next++ = c; + file->available--; + } + else + return -1; + + return c; +} + +local long memTell(MEMFILE *f) +{ + return (f->next - f->memory) - 8; +} + +local int memError(MEMFILE *f) +{ + return f->error; +} + +local int memClose(MEMFILE *f) +{ + if(f->mode == 'w') + { + *((int *)(f->memory+4)) = memTell(f); + } + free(f); + return 0; +} + +local int memPrintf(MEMFILE *f, const char *format, ...) +{ + char buffer[80]; + va_list list; + int len; + + va_start(list, format); + len = vsprintf(buffer, format, list); + va_end(list); + + return memWrite(buffer, 1, len, f); +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open return NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (memory, available, mode) +char *memory; +const int available; +const char *mode; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + mem_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + s = (mem_stream *)ALLOC(sizeof(mem_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->z_err = Z_OK; + s->z_eof = 0; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + s->file = NULL; + + s->mode = '\0'; + do + { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') + { + level = *p - '0'; + } + else if (*p == 'f') + { + strategy = Z_FILTERED; + } + else if (*p == 'h') + { + strategy = Z_HUFFMAN_ONLY; + } + else + { + *m++ = *p; /* copy the mode */ + } + } + while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') + { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) + { + return destroy(s), (gzFile)Z_NULL; + } + } + else + { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) + { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = memOpen(memory, available, s->mode); + + if (s->file == NULL) + { + return destroy(s), (gzFile)Z_NULL; + } + + if (s->mode == 'w') + { + /* Write a very simple .gz header: + */ + memPrintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->startpos = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * startpos anyway in write mode, so this initialization is not + * necessary. + */ + } + else + { + check_header(s); /* skip the .gz header */ + s->startpos = (memTell(s->file) - s->stream.avail_in); + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT memgzopen (memory, available, mode) +char *memory; +int available; +const char *mode; +{ + return gz_open (memory, available, mode); +} + +/* =========================================================================== + Read a byte from a mem_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) +mem_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) + { + errno = 0; + s->stream.avail_in = memRead(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) + { + s->z_eof = 1; + if (memError(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a mem_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) +mem_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Check the gzip magic header */ + for (len = 0; len < 2; len++) + { + c = get_byte(s); + if (c != gz_magic[len]) + { + if (len != 0) s->stream.avail_in++, s->stream.next_in--; + if (c != EOF) + { + s->stream.avail_in++, s->stream.next_in--; + s->transparent = 1; + } + s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; + return; + } + } + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) + { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) + { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) + { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) + { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) + { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + +/* =========================================================================== +* Cleanup then free the given mem_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. +*/ +local int destroy (s) +mem_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) + { + if (s->mode == 'w') + { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } + else if (s->mode == 'r') + { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && memClose(s->file)) + { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT memgzread (file, buf, len) +gzFile file; +voidp buf; +unsigned len; +{ + mem_stream *s = (mem_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + while (s->stream.avail_out != 0) + { + + if (s->transparent) + { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) + { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) + { + s->stream.avail_out -= memRead(next_out, 1, s->stream.avail_out, + s->file); + } + len -= s->stream.avail_out; + s->stream.total_in += (uLong)len; + s->stream.total_out += (uLong)len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) + { + + errno = 0; + s->stream.avail_in = memRead(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) + { + s->z_eof = 1; + if (memError(s->file)) + { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + + if (s->z_err == Z_STREAM_END) + { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) + { + s->z_err = Z_DATA_ERROR; + } + else + { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may + * be different from s->stream.total_out) in case of + * concatenated .gz files. Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) + { + uLong total_in = s->stream.total_in; + uLong total_out = s->stream.total_out; + + inflateReset(&(s->stream)); + s->stream.total_in = total_in; + s->stream.total_out = total_out; + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + return (int)(len - s->stream.avail_out); +} + + +#ifndef NO_DEFLATE +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT memgzwrite (file, buf, len) +gzFile file; +const voidp buf; +unsigned len; +{ + mem_stream *s = (mem_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) + { + + if (s->stream.avail_out == 0) + { + + s->stream.next_out = s->outbuf; + if (memWrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) + { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} +#endif +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) +gzFile file; +int flush; +{ + uInt len; + int done = 0; + mem_stream *s = (mem_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) + { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) + { + if ((uInt)memWrite(s->outbuf, 1, len, s->file) != len) + { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->z_err = deflate(&(s->stream), flush); + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) +MEMFILE *file; +uLong x; +{ + int n; + for (n = 0; n < 4; n++) + { + memPutc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given mem_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) +mem_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT memgzclose (file) +gzFile file; +{ + int err; + mem_stream *s = (mem_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') + { +#ifdef NO_DEFLATE + return Z_STREAM_ERROR; +#else + err = do_flush (file, Z_FINISH); + if (err != Z_OK) return destroy((mem_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, s->stream.total_in); +#endif + } + return destroy((mem_stream*)file); +} + +long ZEXPORT memtell(file) +gzFile file; +{ + mem_stream *s = (mem_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + return memTell(s->file); +} diff --git a/src/memgzio.h b/src/memgzio.h new file mode 100644 index 0000000..2ca74cb --- /dev/null +++ b/src/memgzio.h @@ -0,0 +1,21 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* memgzio.c - IO on .gz files in memory + * Adapted from original gzio.c from zlib library by Forgotten + */ +#ifndef HAVE_ZUTIL_H +#include "../win32/include/zlib/zutil.h" +#else +#include +#endif + +gzFile ZEXPORT memgzopen(char *memory, int, const char *); +int ZEXPORT memgzread(gzFile, voidp, unsigned); +int ZEXPORT memgzwrite(gzFile, const voidp, unsigned); +int ZEXPORT memgzclose(gzFile); +long ZEXPORT memtell(gzFile); diff --git a/src/ngc/gcpad.cpp b/src/ngc/gcpad.cpp new file mode 100644 index 0000000..1057000 --- /dev/null +++ b/src/ngc/gcpad.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +* VisualBoyAdvance 1.7.2 +* +* Nintendo GameCube Joypad Wrapper +****************************************************************************/ +#include +#include + + +#define VBA_BUTTON_A 1 +#define VBA_BUTTON_B 2 +#define VBA_BUTTON_SELECT 4 +#define VBA_BUTTON_START 8 +#define VBA_RIGHT 16 +#define VBA_LEFT 32 +#define VBA_UP 64 +#define VBA_DOWN 128 +#define VBA_BUTTON_R 256 +#define VBA_BUTTON_L 512 +#define VBA_SPEED 1024 +#define VBA_CAPTURE 2048 + +#ifdef WII_BUILD +#include +#include +int isClassicAvailable = 0; +int isWiimoteAvailable = 0; +/* +#ifndef PI +#define PI 3.14159f +#endif + +enum { STICK_X, STICK_Y }; +static int getStickValue(joystick_t* j, int axis, int maxAbsValue){ + double angle = PI * j->ang/180.0f; + double magnitude = (j->mag > 1.0f) ? 1.0f : + (j->mag < -1.0f) ? -1.0f : j->mag; + double value; + if(axis == STICK_X) + value = magnitude * sin( angle ); + else + value = magnitude * cos( angle ); + return (int)(value * maxAbsValue); +}*/ + +#endif +int menuCalled = 0; +u32 +NGCPad() +{ + u32 res = 0; + short p; + signed char x, + y; + int padcal = 90; + float t; + + +#ifdef WII_BUILD + //wiimote + if(isWiimoteAvailable) + { + WPADData wpad; + WPAD_Read(0, &wpad); + unsigned short b = wpad.btns_d; + + if (b & WPAD_BUTTON_2) + res |= VBA_BUTTON_A; + + if (b & WPAD_BUTTON_1) + res |= VBA_BUTTON_B; + + if (b & WPAD_BUTTON_MINUS) + res |= VBA_BUTTON_SELECT; + + if (b & WPAD_BUTTON_PLUS) + res |= VBA_BUTTON_START; + + if (b & WPAD_BUTTON_RIGHT) + res |= VBA_UP; + + if (b & WPAD_BUTTON_LEFT) + res |= VBA_DOWN; + + if (b & WPAD_BUTTON_UP) + res |= VBA_LEFT; + + if (b & WPAD_BUTTON_DOWN) + res |= VBA_RIGHT; + + if (b & WPAD_BUTTON_A) + res |= VBA_BUTTON_L; + + if (b & WPAD_BUTTON_B) + res |= VBA_BUTTON_R; + + if (b & WPAD_BUTTON_HOME) + menuCalled = 1; + } + //classic controller + if(isClassicAvailable) + { + WPADData wpad; + WPAD_Read(0, &wpad); + int b = wpad.exp.classic.btns; + int x_s = 0; //getStickValue(&wpad.exp.classic.ljs, STICK_X, 127); + int y_s = 0; //getStickValue(&wpad.exp.classic.ljs, STICK_Y, 127); + if (b & CLASSIC_CTRL_BUTTON_A) + res |= VBA_BUTTON_A; + + if (b & CLASSIC_CTRL_BUTTON_B) + res |= VBA_BUTTON_B; + + if (b & CLASSIC_CTRL_BUTTON_MINUS) + res |= VBA_BUTTON_SELECT; + + if (b & CLASSIC_CTRL_BUTTON_PLUS) + res |= VBA_BUTTON_START; + + if ((b & CLASSIC_CTRL_BUTTON_UP) || (y_s > 0)) + res |= VBA_UP; + + if ((b & CLASSIC_CTRL_BUTTON_DOWN) || (y_s < 0)) + res |= VBA_DOWN; + + if ((b & CLASSIC_CTRL_BUTTON_LEFT) || (x_s < 0)) + res |= VBA_LEFT; + + if ((b & CLASSIC_CTRL_BUTTON_RIGHT) || (x_s > 0)) + res |= VBA_RIGHT; + + if (b & CLASSIC_CTRL_BUTTON_FULL_L) + res |= VBA_BUTTON_L; + + if (b & CLASSIC_CTRL_BUTTON_FULL_R) + res |= VBA_BUTTON_R; + + if (b & CLASSIC_CTRL_BUTTON_HOME) + menuCalled = 1; + + } + //user needs a GC remote + else if((!(isWiimoteAvailable)) && (!(isClassicAvailable))) + { + p = PAD_ButtonsHeld(0); + x = PAD_StickX(0); + y = PAD_StickY(0); + if (x * x + y * y > padcal * padcal) + { + if (x > 0 && y == 0) + res |= VBA_RIGHT; + if (x < 0 && y == 0) + res |= VBA_LEFT; + if (x == 0 && y > 0) + res |= VBA_UP; + if (x == 0 && y < 0) + res |= VBA_DOWN; + + /*** Recalc left / right ***/ + t = (float) y / x; + if (t >= -2.41421356237 && t < 2.41421356237) + { + if (x >= 0) + res |= VBA_RIGHT; + else + res |= VBA_LEFT; + } + + /*** Recalc up / down ***/ + t = (float) x / y; + if (t >= -2.41421356237 && t < 2.41421356237) + { + if (y >= 0) + res |= VBA_UP; + else + res |= VBA_DOWN; + } + } + if (p & PAD_BUTTON_A) + res |= VBA_BUTTON_A; + + if (p & PAD_BUTTON_B) + res |= VBA_BUTTON_B; + + if (p & PAD_TRIGGER_Z) + res |= VBA_BUTTON_SELECT; + + if (p & PAD_BUTTON_START) + res |= VBA_BUTTON_START; + + if (p & PAD_BUTTON_UP) + res |= VBA_UP; + + if (p & PAD_BUTTON_DOWN) + res |= VBA_DOWN; + + if (p & PAD_BUTTON_LEFT) + res |= VBA_LEFT; + + if (p & PAD_BUTTON_RIGHT) + res |= VBA_RIGHT; + + if (p & PAD_TRIGGER_L) + res |= VBA_BUTTON_L; + + if (p & PAD_TRIGGER_R) + res |= VBA_BUTTON_R; + + if((p & PAD_BUTTON_X) && (p & PAD_BUTTON_Y)) + menuCalled = 1; + } +#endif + +#ifdef GC_BUILD + p = PAD_ButtonsHeld(0); + x = PAD_StickX(0); + y = PAD_StickY(0); + if (x * x + y * y > padcal * padcal) + { + if (x > 0 && y == 0) + res |= VBA_RIGHT; + if (x < 0 && y == 0) + res |= VBA_LEFT; + if (x == 0 && y > 0) + res |= VBA_UP; + if (x == 0 && y < 0) + res |= VBA_DOWN; + + /*** Recalc left / right ***/ + t = (float) y / x; + if (t >= -2.41421356237 && t < 2.41421356237) + { + if (x >= 0) + res |= VBA_RIGHT; + else + res |= VBA_LEFT; + } + + /*** Recalc up / down ***/ + t = (float) x / y; + if (t >= -2.41421356237 && t < 2.41421356237) + { + if (y >= 0) + res |= VBA_UP; + else + res |= VBA_DOWN; + } + } + if (p & PAD_BUTTON_A) + res |= VBA_BUTTON_A; + + if (p & PAD_BUTTON_B) + res |= VBA_BUTTON_B; + + if (p & PAD_TRIGGER_Z) + res |= VBA_BUTTON_SELECT; + + if (p & PAD_BUTTON_START) + res |= VBA_BUTTON_START; + + if ((p & PAD_BUTTON_UP)) + res |= VBA_UP; + + if ((p & PAD_BUTTON_DOWN)) + res |= VBA_DOWN; + + if ((p & PAD_BUTTON_LEFT)) + res |= VBA_LEFT; + + if ((p & PAD_BUTTON_RIGHT)) + res |= VBA_RIGHT; + + if (p & PAD_TRIGGER_L) + res |= VBA_BUTTON_L; + + if (p & PAD_TRIGGER_R) + res |= VBA_BUTTON_R; + + if((p & PAD_BUTTON_X) && (p & PAD_BUTTON_Y)) + menuCalled = 1; +#endif + + if ((res & 48) == 48) + res &= ~16; + if ((res & 192) == 192) + res &= ~128; + + return res; +} diff --git a/src/ngc/gcpad.h b/src/ngc/gcpad.h new file mode 100644 index 0000000..174cfa7 --- /dev/null +++ b/src/ngc/gcpad.h @@ -0,0 +1,11 @@ +/**************************************************************************** +* VisualBoyAdvance 1.7.2 +* +* Nintendo GameCube Joypad Wrapper +****************************************************************************/ +#ifndef __NGCPADH__ +#define __NGCPADH__ + +u32 NGCPad(); + +#endif diff --git a/src/ngc/gx_supp.c b/src/ngc/gx_supp.c new file mode 100644 index 0000000..9d3e4fe --- /dev/null +++ b/src/ngc/gx_supp.c @@ -0,0 +1,264 @@ +/**************************************************************************** +* Generic GX Support for Emulators +* softdev 2007 +* +* 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 2 of the License, or +* (at your option) any later version. +* +* 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 program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +* +* NGC GX Video Functions +* +* These are pretty standard functions to setup and use GX scaling. +****************************************************************************/ +#include +#include +#include +#include +#include + +/*** External 2D Video ***/ +extern u32 whichfb; +extern u32 *xfb[2]; +extern GXRModeObj *vmode; + +/*** 3D GX ***/ +#define DEFAULT_FIFO_SIZE ( 256 * 1024 ) +static u8 gp_fifo[DEFAULT_FIFO_SIZE] ATTRIBUTE_ALIGN(32); + +/*** Texture memory ***/ +static u8 *texturemem; +static int texturesize; + +GXTexObj texobj; +static Mtx view; +static int vwidth, vheight, oldvwidth, oldvheight; + +#define HASPECT 80 +#define VASPECT 60 + +/* New texture based scaler */ +typedef struct tagcamera + { + Vector pos; + Vector up; + Vector view; + } +camera; + +/*** Square Matrix + This structure controls the size of the image on the screen. + Think of the output as a -80 x 80 by -60 x 60 graph. +***/ +static s16 square[] ATTRIBUTE_ALIGN(32) = { + /* + * X, Y, Z + * Values set are for roughly 4:3 aspect + */ + -HASPECT, VASPECT, 0, // 0 + HASPECT, VASPECT, 0, // 1 + HASPECT, -VASPECT, 0, // 2 + -HASPECT, -VASPECT, 0, // 3 + }; + +static camera cam = { {0.0F, 0.0F, 0.0F}, + {0.0F, 0.5F, 0.0F}, + {0.0F, 0.0F, -0.5F} + }; + +/**************************************************************************** + * Scaler Support Functions + ****************************************************************************/ +static void draw_init(void) +{ + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_INDEX8); + GX_SetVtxDesc(GX_VA_CLR0, GX_INDEX8); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + GX_SetArray(GX_VA_POS, square, 3 * sizeof(s16)); + + GX_SetNumTexGens(1); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + GX_InvalidateTexAll(); + + GX_InitTexObj(&texobj, texturemem, vwidth, vheight, GX_TF_RGB565, + GX_CLAMP, GX_CLAMP, GX_FALSE); +} + +static void draw_vert(u8 pos, u8 c, f32 s, f32 t) +{ + GX_Position1x8(pos); + GX_Color1x8(c); + GX_TexCoord2f32(s, t); +} + +static void draw_square(Mtx v) +{ + Mtx m; // model matrix. + Mtx mv; // modelview matrix. + + guMtxIdentity(m); + guMtxTransApply(m, m, 0, 0, -100); + guMtxConcat(v, m, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + draw_vert(0, 0, 0.0, 0.0); + draw_vert(1, 0, 1.0, 0.0); + draw_vert(2, 0, 1.0, 1.0); + draw_vert(3, 0, 0.0, 1.0); + GX_End(); +} + +/**************************************************************************** + * StartGX + ****************************************************************************/ +void GX_Start(int width, int height, int haspect, int vaspect) +{ + Mtx p; + + /*** Set new aspect ***/ + square[0] = square[9] = -haspect; + square[3] = square[6] = haspect; + square[1] = square[4] = vaspect; + square[7] = square[10] = -vaspect; + + /*** Allocate 32byte aligned texture memory ***/ + texturesize = (width * height) * 2; + texturemem = (u8 *) memalign(32, texturesize); + + GXColor gxbackground = { 0, 0, 0, 0xff }; + + /*** Clear out FIFO area ***/ + memset(&gp_fifo, 0, DEFAULT_FIFO_SIZE); + + /*** Initialise GX ***/ + GX_Init(&gp_fifo, DEFAULT_FIFO_SIZE); + GX_SetCopyClear(gxbackground, 0x00ffffff); + + GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1); + GX_SetDispCopyYScale((f32) vmode->xfbHeight / (f32) vmode->efbHeight); + GX_SetScissor(0, 0, vmode->fbWidth, vmode->efbHeight); + GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight); + GX_SetDispCopyDst(vmode->fbWidth, vmode->xfbHeight); + GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, + vmode->vfilter); + GX_SetFieldMode(vmode->field_rendering, + ((vmode->viHeight == + 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE)); + GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); + GX_SetCullMode(GX_CULL_NONE); + GX_CopyDisp(xfb[whichfb ^ 1], GX_TRUE); + GX_SetDispCopyGamma(GX_GM_1_0); + + guPerspective(p, 60, 1.33F, 10.0F, 1000.0F); + GX_LoadProjectionMtx(p, GX_PERSPECTIVE); + memset(texturemem, 0, texturesize); + + /*** Setup for first call to scaler ***/ + vwidth = vheight = -1; + +} + +/**************************************************************************** +* GX_Render +* +* Pass in a buffer, width and height to update as a tiled RGB565 texture +****************************************************************************/ +void GX_Render(int width, int height, u8 * buffer, int pitch) +{ + int h, w; + long long int *dst = (long long int *) texturemem; + long long int *src1 = (long long int *) buffer; + long long int *src2 = (long long int *) (buffer + pitch); + long long int *src3 = (long long int *) (buffer + (pitch * 2)); + long long int *src4 = (long long int *) (buffer + (pitch * 3)); + int rowpitch = (pitch >> 3) * 3; + int rowadjust = ( pitch % 8 ) * 4; + char *ra = NULL; + + vwidth = width; + vheight = height; + + whichfb ^= 1; + + if ((oldvheight != vheight) || (oldvwidth != vwidth)) + { + /** Update scaling **/ + oldvwidth = vwidth; + oldvheight = vheight; + draw_init(); + memset(&view, 0, sizeof(Mtx)); + guLookAt(view, &cam.pos, &cam.up, &cam.view); + GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1); + + } + + GX_InvVtxCache(); + GX_InvalidateTexAll(); + GX_SetTevOp(GX_TEVSTAGE0, GX_DECAL); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + + for (h = 0; h < vheight; h += 4) + { + + for (w = 0; w < (vwidth >> 2); w++) + { + *dst++ = *src1++; + *dst++ = *src2++; + *dst++ = *src3++; + *dst++ = *src4++; + } + + src1 += rowpitch; + src2 += rowpitch; + src3 += rowpitch; + src4 += rowpitch; + + if ( rowadjust ) + { + ra = (char *)src1; + src1 = (long long int *)(ra + rowadjust); + ra = (char *)src2; + src2 = (long long int *)(ra + rowadjust); + ra = (char *)src3; + src3 = (long long int *)(ra + rowadjust); + ra = (char *)src4; + src4 = (long long int *)(ra + rowadjust); + } + } + + DCFlushRange(texturemem, texturesize); + + GX_SetNumChans(1); + GX_LoadTexObj(&texobj, GX_TEXMAP0); + + draw_square(view); + + GX_DrawDone(); + + GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_CopyDisp(xfb[whichfb], GX_TRUE); + GX_Flush(); + + VIDEO_SetNextFramebuffer(xfb[whichfb]); + VIDEO_Flush(); + //VIDEO_WaitVSync(); + +} diff --git a/src/ngc/gx_supp.h b/src/ngc/gx_supp.h new file mode 100644 index 0000000..36c0cfb --- /dev/null +++ b/src/ngc/gx_supp.h @@ -0,0 +1,29 @@ +/**************************************************************************** +* Generic GX Scaler +* softdev 2007 +* +* 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 2 of the License, or +* (at your option) any later version. +* +* 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 program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +* +* NGC GX Video Functions +* +* These are pretty standard functions to setup and use GX scaling. +****************************************************************************/ +#ifndef __GXHDR__ +#define __GXHDR__ + +void GX_Start(int width, int height, int haspect, int vaspect); +void GX_Render(int width, int height, u8 * buffer, int pitch); + +#endif diff --git a/src/ngc/main.c b/src/ngc/main.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/ngc/main.c @@ -0,0 +1 @@ + diff --git a/src/ngc/menu.cpp b/src/ngc/menu.cpp new file mode 100644 index 0000000..04f68c5 --- /dev/null +++ b/src/ngc/menu.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +* File Selection Menu +* +****************************************************************************/ +#include +#include +#include +#include +#include "sdfileio.h" + +extern GXRModeObj *vmode; /*** Graphics Mode Object ***/ +extern u32 *xfb[2]; /*** Framebuffers ***/ +extern int whichfb; /*** Frame buffer toggle ***/ + +/** Bits from SD lib **/ + +#define PAGE_SIZE 14 + +/*** Use OGC built in font ***/ +extern u8 console_font_8x16[]; + +static u32 forecolour = COLOR_WHITE; +static u32 backcolour = COLOR_BLACK; + +/**************************************************************************** +* MENU_DrawChar +****************************************************************************/ +void MENU_DrawChar( int x, int y, char c, int style ) +{ + u8 bits; + int offset; + int h,w; + u32 colour[2]; + u32 scroffs; + + offset = c << 4; + scroffs = ( y * 320 ) + ( x >> 1 ); + + for( h = 0; h < 16; h++ ) + { + bits = console_font_8x16[ offset++ ]; + + if ( style ) + { + for( w = 0; w < 8; w++ ) + { + xfb[whichfb][scroffs + w] = ( bits & 0x80 ) ? forecolour : backcolour; + bits <<= 1; + } + } + else + { + for( w = 0; w < 4; w++ ) + { + colour[0] = ( bits & 0x80 ) ? forecolour : backcolour; + colour[1] = ( bits & 0x40 ) ? forecolour : backcolour; + + xfb[whichfb][scroffs + w] = ( colour[0] & 0xFFFF00FF ) | ( colour[1] & 0x0000FF00 ); + bits <<= 2; + } + } + + scroffs += 320; + } +} + +/**************************************************************************** +* MENU_DrawString +****************************************************************************/ +void MENU_DrawString( int x, int y, char *msg, int style ) +{ + int i,len; + + /* Centred ? */ + if ( x == -1 ) + { + if ( style ) + x = strlen(msg) << 4; + else + x = strlen(msg) << 3; + + x = ( 640 - x ) >> 1; + } + if((int)strlen(msg) < 36) + len = (int)strlen(msg); + else + len = 36; + for ( i = 0; i < len; i++ ) + { + MENU_DrawChar(x,y,msg[i],style); + x += ( style ? 16 : 8 ); + } +} + +/**************************************************************************** +* MENU_Draw +****************************************************************************/ +int MENU_Draw( int max, int current, int offset ) +{ + int i; + int ypos = 30; + int xpos = 30; + + for ( i = offset; i < max && ( ( i - offset ) < PAGE_SIZE ); i++ ) + { + if ( i == current ) + { + forecolour = COLOR_BLACK; + backcolour = COLOR_WHITE; + } + else + { + forecolour = COLOR_WHITE; + backcolour = COLOR_BLACK; + } + MENU_DrawString( xpos, ypos, direntries[i], 1); + ypos += 16; + } +} +#ifdef WII_BUILD +#include +#include +extern int isClassicAvailable; +extern int isWiimoteAvailable; +extern void setup_controllers(); +#endif + +/**************************************************************************** +* MENU_GetLoadFile +* +* Returns the filename of the selected file +***************************************************************************/ +char *MENU_GetLoadFile( char *whichdir ) +{ + int count; + char *p = NULL; + int quit = 0; + int redraw = 1; + int current = 0; + int offset = 0; + u16 buttons; + int do_DOWN = 0; + int do_UP = 0; + int do_A = 0; + + count = gen_getdir( whichdir ); + + + if ( count == 0 ) + { + printf("No ROM files in %s\n", whichdir); + while(1); + } + + if ( count == 1 ) + return (char*)direntries[0]; + + + /* Do menu */ + while ( !quit ) + { + if ( redraw ) + { + whichfb ^= 1; + VIDEO_ClearFrameBuffer(vmode, xfb[whichfb], COLOR_BLACK); + MENU_Draw( count, current, offset ); + VIDEO_SetNextFramebuffer(xfb[whichfb]); + VIDEO_Flush(); + VIDEO_WaitVSync(); + redraw = 0; + } +#ifdef WII_BUILD + setup_controllers(); + WPADData wpad; + WPAD_Read(0, &wpad); + if(isClassicAvailable) + { + int b = wpad.exp.classic.btns; + if (b & CLASSIC_CTRL_BUTTON_DOWN){ + do_DOWN = 1; + do{WPAD_Read(0, &wpad); b = wpad.exp.classic.btns;}while(b & CLASSIC_CTRL_BUTTON_DOWN); + } + else if (b & CLASSIC_CTRL_BUTTON_UP){ + do_UP = 1; + do{WPAD_Read(0, &wpad); b = wpad.exp.classic.btns;}while(b & CLASSIC_CTRL_BUTTON_UP); + } + else if (b & CLASSIC_CTRL_BUTTON_A){ + do_A = 1; + do{WPAD_Read(0, &wpad); b = wpad.exp.classic.btns;}while(b & CLASSIC_CTRL_BUTTON_A); + } + } + if(isWiimoteAvailable) + { + unsigned short b = wpad.btns_d; + if(b & WPAD_BUTTON_LEFT){ + do_DOWN = 1; + do{WPAD_Read(0, &wpad); b = wpad.btns_d;}while(b & WPAD_BUTTON_LEFT); + } + else if (b & WPAD_BUTTON_RIGHT){ + do_UP = 1; + do{WPAD_Read(0, &wpad); b = wpad.btns_d;}while(b & WPAD_BUTTON_RIGHT); + } + else if (b & WPAD_BUTTON_2){ + do_A = 1; + do{WPAD_Read(0, &wpad); b = wpad.btns_d;}while(b & WPAD_BUTTON_2); + } + } +#endif + buttons = PAD_ButtonsDown(0); + if(buttons & PAD_BUTTON_DOWN) + do_DOWN = 1; + else if(buttons & PAD_BUTTON_UP) + do_UP = 1; + else if(buttons & PAD_BUTTON_A) + do_A = 1; + if (do_DOWN) + { + do_DOWN=0; + current++; + if ( current == count ) + current = 0; + + if ( ( current % PAGE_SIZE ) == 0 ) + offset = current; + + redraw = 1; + } + else + { + if ( do_UP ) + { + do_UP=0; + current--; + if ( current < 0 ) current = count - 1; + offset = ( current / PAGE_SIZE ) * PAGE_SIZE; + redraw = 1; + } + else + { + if ( do_A ) + { + do_A=0; + quit = 1; + p = (char*)direntries[current]; + } + } + } + } + + whichfb ^= 1; + VIDEO_ClearFrameBuffer(vmode, xfb[whichfb], COLOR_BLACK); + forecolour = COLOR_WHITE; + backcolour = COLOR_BLACK; + MENU_DrawString(-1, 240, "Loading ... Wait", 1); + VIDEO_SetNextFramebuffer(xfb[whichfb]); + VIDEO_Flush(); + VIDEO_WaitVSync(); + return p; +} + diff --git a/src/ngc/mixer.cpp b/src/ngc/mixer.cpp new file mode 100644 index 0000000..64e7868 --- /dev/null +++ b/src/ngc/mixer.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +* VisualBoyAdvance +* +* Head and tail audio mixer +****************************************************************************/ +#include +#include +#include +#include + +/** Locals **/ +static int head = 0; +static int tail = 0; + +#define MIXBUFFSIZE 0x10000 +static u8 mixerdata[MIXBUFFSIZE]; +#define MIXERMASK ((MIXBUFFSIZE >> 2) - 1) + +/**************************************************************************** +* MIXER_AddSamples +* +* Upsample from 11025 to 48000 +* 11025 == 15052 +* 22050 == 30106 +* 44100 == 60211 +* +* Audio officianados should look away now ! +****************************************************************************/ +void MIXER_AddSamples( u8 *sampledata, int len ) +{ + u32 *src = (u32 *)sampledata; + u32 *dst = (u32 *)mixerdata; + u32 intlen = (3200 >> 2); + u32 fixofs = 0; + u32 fixinc; + + if ( !len ) + fixinc = 30106; + else + fixinc = 60211; + + do + { + /* Do simple linear interpolate */ + dst[head++] = src[fixofs >> 16]; + head &= MIXERMASK; + fixofs += fixinc; + } + while( --intlen ); + +} + +/**************************************************************************** +* MIXER_GetSamples +****************************************************************************/ +int MIXER_GetSamples( u8 *dstbuffer, int maxlen ) +{ + u32 *src = (u32 *)mixerdata; + u32 *dst = (u32 *)dstbuffer; + u32 intlen = maxlen >> 2; + + memset(dstbuffer, 0, maxlen); + + while( ( head != tail ) && intlen ) + { + *dst++ = src[tail++]; + tail &= MIXERMASK; + intlen--; + } + + return 3200; +} + diff --git a/src/ngc/mixer.h b/src/ngc/mixer.h new file mode 100644 index 0000000..227107e --- /dev/null +++ b/src/ngc/mixer.h @@ -0,0 +1,13 @@ +/**************************************************************************** +* VisualBoyAdvance +* +* Head and tail audio mixer +****************************************************************************/ +#ifndef __AUDIOMIXER__ +#define __AUDIOMIXER__ + +void MIXER_AddSamples( u8 *sampledata, int len ); +int MIXER_GetSamples( u8 *dstbuffer, int maxlen ); + +#endif + diff --git a/src/ngc/pal60.h b/src/ngc/pal60.h new file mode 100644 index 0000000..160873c --- /dev/null +++ b/src/ngc/pal60.h @@ -0,0 +1,85 @@ +u32 vpal60[] = + { + 0x0e160001, + 0x476901ad, + 0x02ea5140, + 0x00110028, + 0x00100029, + 0x410c410c, + 0x40ed40ed, + 0x002f8ee0, + 0x00000000, + 0x002f93e0, + 0x00000000, + 0x00e20262, + 0x910701ae, + 0x90010001, + 0x00010001, + 0x00010001, + 0x00000000, + 0x00000000, + 0x28500100, + 0x1ae771f0, + 0x0db4a574, + 0x00c1188e, + 0xc4c0cbe2, + 0xfcecdecf, + 0x13130f08, + 0x00080c0f, + 0x00ff0000, + 0x00000000, + 0x02800000, + 0x000000ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff, + 0x00ff00ff + }; + +/**************************************************************************** +* VIDEO60 +* +* Differences between Wii/GC and libOGC 60Hz I modes +* +* CC002000 0f06 0e16 +* CC00200C 0003 0011 +* CC00200E 0018 0028 +* CC002010 0002 0010 +* CC002012 0019 0029 +* CC00201C 0034 61A0 002F 8EE0 +* CC002024 0034 66A0 002F 93E0 +* CC00202C 00E4 001E 00E2 0262 +* CC002030 1107 01AE 9107 01AE +* CC002034 1001 0001 9001 0001 +****************************************************************************/ + diff --git a/src/ngc/tbtime.c b/src/ngc/tbtime.c new file mode 100644 index 0000000..d41eba0 --- /dev/null +++ b/src/ngc/tbtime.c @@ -0,0 +1,15 @@ +#include "tbtime.h" + +unsigned long +tb_diff_msec (tb_t * end, tb_t * start) +{ + unsigned long upper, lower; + upper = end->u - start->u; + if (start->l > end->l) + upper--; + lower = end->l - start->l; + return ((upper * ((unsigned long) 0x80000000 / (TB_CLOCK / 2000))) + + (lower / (TB_CLOCK / 1000))); +} + + diff --git a/src/ngc/tbtime.h b/src/ngc/tbtime.h new file mode 100644 index 0000000..fac67aa --- /dev/null +++ b/src/ngc/tbtime.h @@ -0,0 +1,27 @@ +/**************************************************************************** +* tmbinc msec timer +****************************************************************************/ +#ifndef __TMBINCTIMER__ +#define __TMBINCTIMER__ +#ifdef WII_BUILD +#define TB_CLOCK 60750000 //WII +#endif +#ifdef GC_BUILD +#define TB_CLOCK 40500000 +#endif +#define mftb(rval) ({unsigned long u; do { \ + asm volatile ("mftbu %0" : "=r" (u)); \ + asm volatile ("mftb %0" : "=r" ((rval)->l)); \ + asm volatile ("mftbu %0" : "=r" ((rval)->u)); \ + } while(u != ((rval)->u)); }) + +typedef struct + { + unsigned long l, u; + } +tb_t; + +unsigned long tb_diff_msec (tb_t * end, tb_t * start); + +#endif + diff --git a/src/ngc/vba172.cpp b/src/ngc/vba172.cpp new file mode 100644 index 0000000..5f139e5 --- /dev/null +++ b/src/ngc/vba172.cpp @@ -0,0 +1,790 @@ +/**************************************************************************** +* VisualBoyAdvance 1.7.2 +* Nintendo GameCube Wrapper +****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#ifdef WII_BUILD + +extern "C" + { +#include +#include +#include +extern s32 CONF_Init(void); + } + +extern int isClassicAvailable; +extern int isWiimoteAvailable; + +void setup_controllers() +{ + WPADData wpad; + WPAD_Read(0, &wpad); + // User will have to use a GC controller + if(wpad.err == WPAD_ERR_NO_CONTROLLER) + { + isClassicAvailable = 0; + isWiimoteAvailable = 0; + return; + } + // User can use just wiimote + if(wpad.err == WPAD_ERR_NONE && wpad.exp.type == WPAD_EXP_NONE) + { + isClassicAvailable = 0; + isWiimoteAvailable = 1; + return; + } + + // User can use a Classic controller + if(wpad.err == WPAD_ERR_NONE && wpad.exp.type == WPAD_EXP_CLASSIC) + { + isClassicAvailable = 1; + WPAD_SetDataFormat(0, WPAD_FMT_CORE); + return; + } + +} +#endif + +#include "mixer.h" +#include "gcpad.h" +#include "vmmem.h" +#include "pal60.h" + + +extern "C" + { +#include "gx_supp.h" +#include "tbtime.h" + } + +/** VBA **/ +#include "GBA.h" +#include "agbprint.h" +#include "Flash.h" +#include "Port.h" +#include "RTC.h" +#include "Sound.h" +#include "Text.h" +#include "unzip.h" +#include "Util.h" +#include "gb/GB.h" +#include "gb/gbGlobals.h" + +/** + * Globals + */ +int RGB_LOW_BITS_MASK=0x821; +int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +u16 systemGbPalette[24]; +int systemDebug = 0; +int emulating = 0; +int systemFrameSkip = 0; +int systemRedShift = 0; +int systemBlueShift = 0; +int systemGreenShift = 0; +int systemColorDepth = 0; +int sensorX = 2047; +int sensorY = 2047; +u16 systemColorMap16[0x10000]; +//u32 systemColorMap32[0x10000]; +u32 *systemColorMap32 = (u32 *)&systemColorMap16; +bool systemSoundOn = false; +int systemVerbose = 0; +int cartridgeType = 0; +int srcWidth = 0; +int srcHeight = 0; +int destWidth = 0; +int destHeight = 0; +int srcPitch = 0; +extern int menuCalled; +#define WITHGX 1 +#define DEBUGON 0 + +#if DEBUGON +const char *dbg_local_ip = "192.168.1.32"; +const char *dbg_netmask = "255.255.255.0"; +const char *dbg_gw = "192.168.1.100"; +#endif + +/*** 2D Video Globals ***/ +GXRModeObj *vmode; /*** Graphics Mode Object ***/ +u32 *xfb[2] = { NULL, NULL }; /*** Framebuffers ***/ +int whichfb = 0; /*** Frame buffer toggle ***/ + +void debuggerOutput(char *, u32) +{} + +void (*dbgOutput)(char *, u32) = debuggerOutput; + +/** + * Locals + */ +extern "C" + { + void SDInit( void ); + int gen_getdir( char *whichdir ); + }; +extern char *direntries[1000]; +extern char *MENU_GetLoadFile( char *whichdir ); +extern void MENU_DrawString( int x, int y, char *msg, int style ); + +struct EmulatedSystem emulator = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + false, + 0 + }; + +static tb_t start, now; +static u8 soundbuffer[3200] ATTRIBUTE_ALIGN(32); + +u32 loadtimeradjust; + +void doScan(u32 blah) +{ + PAD_ScanPads(); +} + +/**************************************************************************** +* Initialise Video +* +* Before doing anything in libogc, it's recommended to configure a video +* output. +****************************************************************************/ +static void +Initialise (void) +{ + VIDEO_Init (); /*** ALWAYS CALL FIRST IN ANY LIBOGC PROJECT! + Not only does it initialise the video + subsystem, but also sets up the ogc os + ***/ +#if DEBUGON + DEBUG_Init(2424); + _break(); +#endif + + PAD_Init (); /*** Initialise pads for input ***/ +#ifdef WII_BUILD + CONF_Init(); + WPAD_Init(); +#endif + vmode = &TVNtsc480IntDf; + + /*** Let libogc configure the mode ***/ + VIDEO_Configure (vmode); + + /*** Now configure the framebuffer. + Really a framebuffer is just a chunk of memory + to hold the display line by line. + ***/ + + xfb[0] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); + /*** I prefer also to have a second buffer for double-buffering. + This is not needed for the console demo. + ***/ + xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); + + /*** Define a console ***/ + console_init (xfb[0], 20, 64, vmode->fbWidth, vmode->xfbHeight, + vmode->fbWidth * 2); + + /*** Clear framebuffer to black ***/ + VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK); + VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK); + + /*** Set the framebuffer to be displayed at next VBlank ***/ + VIDEO_SetNextFramebuffer (xfb[0]); + + /*** Get the PAD status updated by libogc ***/ + VIDEO_SetPreRetraceCallback (doScan); + VIDEO_SetBlack (0); + + int i; + u32 *vreg = (u32 *)0xCC002000; + for ( i = 0; i < 64; i++ ) + vreg[i] = vpal60[i]; + + /*** Update the video for next vblank ***/ + VIDEO_Flush (); + + VIDEO_WaitVSync (); /*** Wait for VBL ***/ + if (vmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync (); + +} + +void systemMessage(int num, const char *msg, ...) +{ + /*** For now ... do nothing ***/ +} +void systemFrame() +{} + +void systemScreenCapture(int a) +{} + +void systemShowSpeed(int speed) +{} +static u32 autoFrameSkipLastTime = 0; +static int frameskipadjust = 0; +void system1Frame(int rate) +{ +/* if ( cartridgeType == 1 ) + return; + + u32 time = systemGetClock(); + u32 diff = time - autoFrameSkipLastTime; + int speed = 100; + + if(diff) + speed = (1000000/rate)/diff; + char temp[512]; + sprintf(temp,"Speed: %i",speed); + MENU_DrawString( -1, 450,temp , 1 ); + + if(speed >= 98) + { + frameskipadjust++; + + if(frameskipadjust >= 3) + { + frameskipadjust=0; + if(systemFrameSkip > 0) + systemFrameSkip--; + } + } + else + { + if(speed < 80) + frameskipadjust -= (90 - speed)/5; + else if(systemFrameSkip < 9) + frameskipadjust--; + + if(frameskipadjust <= -2) + { + frameskipadjust += 2; + if(systemFrameSkip < 9) + systemFrameSkip++; + } + } + autoFrameSkipLastTime = time; +*/ +} + +void system10Frames(int rate) +{ + if ( cartridgeType == 1 ) + return; + + u32 time = systemGetClock(); + u32 diff = time - autoFrameSkipLastTime; + int speed = 100; + + if(diff) + speed = (1000000/rate)/diff; +/* char temp[512]; + sprintf(temp,"Speed: %i",speed); + MENU_DrawString( -1, 450,temp , 1 ); +*/ + if(speed >= 98) + { + frameskipadjust++; + + if(frameskipadjust >= 3) + { + frameskipadjust=0; + if(systemFrameSkip > 0) + systemFrameSkip--; + } + } + else + { + if(speed < 80) + frameskipadjust -= (90 - speed)/5; + else if(systemFrameSkip < 9) + frameskipadjust--; + + if(frameskipadjust <= -2) + { + frameskipadjust += 2; + if(systemFrameSkip < 9) + systemFrameSkip++; + } + } + autoFrameSkipLastTime = time; + +} + +void systemUpdateMotionSensor() +{} + +void systemGbBorderOn() +{} + +void systemWriteDataToSoundBuffer() +{ + MIXER_AddSamples((u8 *)soundFinalWave, (cartridgeType == 1)); +} + +void systemSoundPause() +{} + +void systemSoundResume() +{} + +void systemSoundReset() +{} + +void systemGbPrint(u8 *data,int pages,int feed,int palette, int contrast) +{} + +void systemSoundShutdown() +{} + +static void AudioPlayer( void ) +{ + AUDIO_StopDMA(); + MIXER_GetSamples(soundbuffer, 3200); + DCFlushRange(soundbuffer,3200); + AUDIO_InitDMA((u32)soundbuffer,3200); + AUDIO_StartDMA(); +} + +bool systemSoundInit() +{ + AUDIO_Init(NULL); + AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); + AUDIO_RegisterDMACallback(AudioPlayer); + memset(soundbuffer, 0, 3200); + + //printf("Audio Inited\n"); + return true; +} + +static void AudioDeInit() +{ + AUDIO_StopDMA(); +} + +bool systemPauseOnFrame() +{ + return false; +} + +int systemGetSensorX() +{ + return sensorX; +} + +int systemGetSensorY() +{ + return sensorY; +} + +bool systemCanChangeSoundQuality() +{ + return true; +} + +/**************************************************************************** +* systemReadJoypads +****************************************************************************/ +bool systemReadJoypads() +{ + return true; +} + +u32 systemReadJoypad(int which) +{ + return NGCPad(); +} + +/**************************************************************************** +* systemGetClock +* +* Returns number of milliseconds since program start +****************************************************************************/ +u32 systemGetClock( void ) +{ + mftb(&now); + return tb_diff_msec(&now, &start) - loadtimeradjust; +} + +/**************************************************************************** +* systemDrawScreen +****************************************************************************/ +void systemDrawScreen() +{ + /** GB / GBC Have oodles of time - so sync on VSync **/ +#if WITHGX + GX_Render( srcWidth, srcHeight, pix, srcPitch ); +#endif +#ifdef WII_BUILD + VIDEO_WaitVSync (); +#endif +#ifdef GC_BUILD + if ( cartridgeType == 1 ) + { + VIDEO_WaitVSync(); + } +#endif +} + +/**************************************************************************** +* Showdir +****************************************************************************/ +int ShowDir( char *whichdir ) +{ + int count; + int i; + count = gen_getdir( whichdir ); + printf("Found %d files\n",count); + + for ( i = 0; i < count; i++ ) + printf("%s\n", direntries[i]); + while(1); +} + +char filename[1024]; //rom file name +char batfilename[1024]; //battery save file name + + +/* + Asks user to select a ROM +*/ +void chooseRom() { + //char *fname = "Sonic Advance 3.GBA"; + char *fname = NULL; + memset(filename,0,1024); + memset(batfilename,0,1024); + fname = MENU_GetLoadFile( "/VBA/ROMS" ); + sprintf(filename, "/VBA/ROMS/%s", fname); + + // construct battery save file name + strcpy(batfilename, "/VBA/SAVES/"); + strncat(batfilename,fname,strlen(fname)-4); + strcat(batfilename,".SAV"); + +} + +void askSound() { + soundOffFlag = false; + soundLowPass = true; +} + +/* + Checks to see if a previous SRAM/Flash save exists + If it does, it prompts the user to load it or not +*/ +void checkSave() { + +} +void GC_Sleep(u32 dwMiliseconds) +{ + int nVBlanks = (dwMiliseconds / 16); + while (nVBlanks-- > 0) + { + VIDEO_WaitVSync(); + } +} +void write_save() +{ + VIDEO_ClearFrameBuffer (vmode, xfb[whichfb], COLOR_BLACK); + MENU_DrawString( -1, 250,"Attempting to Save data ..." , 1 ); + whichfb^=1; + VIDEO_ClearFrameBuffer (vmode, xfb[whichfb], COLOR_BLACK); + if(emulator.emuWriteBattery(batfilename) == true) + MENU_DrawString( -1, 250,"Save OK!" , 1 ); + else + MENU_DrawString( -1, 250,"Either no data to save yet or error!" , 1 ); + VIDEO_WaitVSync (); + VIDEO_SetNextFramebuffer(xfb[whichfb]); + VIDEO_Flush(); + GC_Sleep(2000); + +} + +int ingameMenu() { + tb_t start,end; + mftb(&start); + char temp[512]; + u16 buttons; + AudioDeInit(); + whichfb^=1; + VIDEO_ClearFrameBuffer (vmode, xfb[whichfb], COLOR_BLACK); + MENU_DrawString( 140, 50,"VisualBoyAdvance 1.7.2" , 1 ); + +#ifdef GC_BUILD + MENU_DrawString( 140, 80,"Nintendo Gamecube Port" , 1 ); + MENU_DrawString( -1, 170,"(B) - Resume play" , 1 ); + MENU_DrawString( -1, 200,"(L) - Write save" , 1 ); + MENU_DrawString( -1, 230,"(R) - Reset game" , 1 ); + MENU_DrawString( -1, 320,"(Z) - Return to loader" , 1 ); +#endif +#ifdef WII_BUILD + MENU_DrawString( 140, 80,"Nintendo Wii Port" , 1 ); + MENU_DrawString( -1, 170,"(B) or (Home) - Resume play" , 1 ); + MENU_DrawString( -1, 200,"(L) or (+) - Write save" , 1 ); + MENU_DrawString( -1, 230,"(R) or (-) - Reset game" , 1 ); + MENU_DrawString( -1, 320,"(Z) or (A+B) - Return to loader" , 1 ); +#endif + + sprintf(temp,"Frameskip: Auto (Currently %i)",systemFrameSkip); + MENU_DrawString( -1, 450,temp , 1 ); + + VIDEO_WaitVSync (); + VIDEO_SetNextFramebuffer(xfb[whichfb]); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + //wait for user to let go of menu calling button + do{buttons = PAD_ButtonsHeld(0);}while((buttons & PAD_BUTTON_A)||(buttons & PAD_BUTTON_B)); + //wait for user to let go of home button +#ifdef WII_BUILD + WPADData wpad; + int btn; + if(isWiimoteAvailable) + do{WPAD_Read(0, &wpad); btn = wpad.btns_d;}while(btn & WPAD_BUTTON_HOME); + if(isClassicAvailable) + do{WPAD_Read(0, &wpad); btn = wpad.exp.classic.btns;}while(btn & CLASSIC_CTRL_BUTTON_HOME); +#endif + + while(1){ + +#ifdef WII_BUILD + WPADData wpad; + WPAD_Read(0, &wpad); + if(isWiimoteAvailable) + { + unsigned short b = wpad.btns_d; + if(b & WPAD_BUTTON_MINUS){ //Reset game + emulator.emuReset(); + break; + } + if(b & WPAD_BUTTON_PLUS) { //Write save + write_save(); + break; + } + if((b & WPAD_BUTTON_A) && (b & WPAD_BUTTON_B)) { //Return to loader + void (*reload)() = (void(*)())0x80001800; + reload(); + } + if(b & WPAD_BUTTON_HOME) { //Resume play + do{WPAD_Read(0, &wpad); b = wpad.btns_d;}while(b & WPAD_BUTTON_HOME); //wait for home + break; + } + } + if(isClassicAvailable) + { + int b = wpad.exp.classic.btns; + if(b & CLASSIC_CTRL_BUTTON_MINUS){ //Reset game + emulator.emuReset(); + break; + } + if(b & CLASSIC_CTRL_BUTTON_PLUS) { //Write save + write_save(); + break; + } + if((b & CLASSIC_CTRL_BUTTON_A) && (b & CLASSIC_CTRL_BUTTON_B)) { //Return to loader + void (*reload)() = (void(*)())0x80001800; + reload(); + } + if(b & CLASSIC_CTRL_BUTTON_HOME) { //Resume play + do{WPAD_Read(0, &wpad);}while(wpad.exp.classic.btns & CLASSIC_CTRL_BUTTON_HOME); //wait for home button + break; + } + } +#endif + + u16 buttons = PAD_ButtonsHeld(0); //grab pad buttons + + if(buttons & PAD_TRIGGER_R){ //Reset game + emulator.emuReset(); + break; + } + if(buttons & PAD_TRIGGER_L) { //Write save + write_save(); + break; + } + if(buttons & PAD_TRIGGER_Z) { //Return to loader + void (*reload)() = (void(*)())0x80001800; + reload(); + } + if(buttons & PAD_BUTTON_B) { //Resume play + break; + } + } + AudioPlayer(); + mftb(&end); + loadtimeradjust += tb_diff_msec(&end, &start); + return 0; + +} + +/**************************************************************************** +* main +* +* Program entry +****************************************************************************/ +int main() +{ + int i; + int vAspect = 0; + int hAspect = 0; + + Initialise(); + + printf("\n\n\nVisualBoyAdvance 1.7.2\n"); + printf("Nintendo Wii Port\n"); + + + /** Kick off SD Lib **/ + SDInit(); + + /** Build GBPalette **/ + for( i = 0; i < 24; ) + { + systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10); + systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10); + systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10); + systemGbPalette[i++] = 0; + } + /** Set palette etc - Fixed to RGB565 **/ + systemColorDepth = 16; + systemRedShift = 11; + systemGreenShift = 6; + systemBlueShift = 0; + for(i = 0; i < 0x10000; i++) + { + systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + +#ifdef WII_BUILD + setup_controllers(); +#endif + //Main loop + while(1) { + cartridgeType = 0; + srcWidth = 0; + srcHeight = 0; + destWidth = 0; + destHeight = 0; + srcPitch = 0; + + chooseRom(); + //checkSave(); + + IMAGE_TYPE type = utilFindType(filename); + + switch( type ) + { + case IMAGE_GBA: +// printf("GameBoy Advance Image\n"); + cartridgeType = 2; + emulator = GBASystem; + srcWidth = 240; + srcHeight = 160; + VMCPULoadROM(filename); + /* Actual Visual Aspect is 1.57 */ + hAspect = 70; + vAspect = 46; + srcPitch = 484; + soundQuality = 2; + soundBufferLen = 1470; + cpuSaveType = 0; + break; + + case IMAGE_GB: +// printf("GameBoy Image\n"); + cartridgeType = 1; + emulator = GBSystem; + srcWidth = 160; + srcHeight = 144; + gbLoadRom(filename); + /* Actual physical aspect is 1.0 */ + hAspect = 60; + vAspect = 46; + srcPitch = 324; + soundQuality = 1; + soundBufferLen = 1470 * 2; + break; + + default: +// printf("Unknown Image\n"); + while(1); + break; + + } + + /** Set defaults **/ + flashSetSize(0x10000); + rtcEnable(true); + agbPrintEnable(false); + askSound(); + + #if WITHGX + /** Set GX **/ + GX_Start( srcWidth, srcHeight, hAspect, vAspect ); + #endif + + if ( cartridgeType == 1 ) + { + gbSoundReset(); + gbSoundSetQuality(soundQuality); + } + else + { + soundSetQuality(soundQuality); + CPUInit("/VBA/BIOS/BIOS.GBA", 1); + CPUReset(); + } + + soundVolume = 0; + systemSoundOn = true; + + soundInit(); + AudioPlayer(); + + emulating = 1; + /** Start system clock **/ + mftb(&start); + + emulator.emuReadBattery(batfilename); + while ( emulating ) + { + emulator.emuMain(emulator.emuCount); + if(menuCalled) { + if(ingameMenu()) + break; + menuCalled = 0; + } + } + + } + + /** Never **/ + while(1); + return 0; + +} + diff --git a/src/ngc/vmmem.cpp b/src/ngc/vmmem.cpp new file mode 100644 index 0000000..d991316 --- /dev/null +++ b/src/ngc/vmmem.cpp @@ -0,0 +1,595 @@ +#ifdef WII_BUILD +/**************************************************************************** +* VisualBoyAdvance 1.7.2 +* +* GameBoy Advance Virtual Memory Paging +****************************************************************************/ +#include "sdfileio.h" +#include +#include +#include +#include +#include +#include + +#include "GBA.h" +#include "Globals.h" +#include "Util.h" +#include "Port.h" + +extern "C" { +#include "tbtime.h" +} + +#define MEM_BAD 0xff +#define MEM_VM 0x01 +#define MEM_UN 0x80 + +unsigned int MEM2Storage = 0x91000000; + +static char *gbabase = NULL; +static FILE *romfile = NULL; +static u32 GBAROMSize = 0; +static char romfilename[1024]; + +/** + * GBA Memory + */ +#define WORKRAM 0x40000 +#define BIOS 0x4000 +#define INTERNALRAM 0x8000 +#define PALETTERAM 0x400 +#define VRAM 0x20000 +#define OAM 0x400 +#define PIX (4 * 241 * 162) +#define IOMEM 0x400 +#define GBATOTAL (WORKRAM + BIOS + INTERNALRAM + PALETTERAM + \ + VRAM + OAM + PIX + IOMEM ) + +extern void CPUUpdateRenderBuffers(bool force); +extern void MENU_DrawString( int x, int y, char *msg, int style ); +/**************************************************************************** +* VMAllocGBA +* +* Allocate the memory required for GBA. +****************************************************************************/ +static void VMAllocGBA( void ) +{ + gbabase = (char *)memalign(32, GBATOTAL); + memset(gbabase, 0, GBATOTAL); + + /* Assign to internal GBA variables */ + workRAM = (u8 *)gbabase; + bios = (u8 *)(gbabase + WORKRAM); + internalRAM = (u8 *)(bios + BIOS); + paletteRAM = (u8 *)(internalRAM + INTERNALRAM); + vram = (u8 *)(paletteRAM + PALETTERAM); + oam = (u8 *)(vram + VRAM); + pix = (u8 *)(oam + OAM); + ioMem = (u8 *)(pix + PIX); +} + + +/**************************************************************************** +* VMClose +****************************************************************************/ +static void VMClose( void ) +{ +/* if ( rombase != NULL ) + free(rombase); +*/ + if ( gbabase != NULL ) + free(gbabase); + + if ( romfile != NULL ) + gen_fclose(romfile); + + gbabase = NULL; + romfile = NULL; + +} + +/**************************************************************************** +* VMCPULoadROM +* +* MEM2 version of GBA CPULoadROM +****************************************************************************/ +int VMCPULoadROM( char *filename ) +{ + int res=0; + char temp[512]; + VMClose(); + VMAllocGBA(); + + GBAROMSize = 0; + + /*sprintf(temp,"Filename %s\n", filename); + MENU_DrawString( -1, 230,temp , 1 ); + */ + romfile = gen_fopen(filename, "rb"); + if ( romfile == NULL ) + { + MENU_DrawString( -1, 400,"Error opening file!" , 1 ); + while(1); + VMClose(); + return 0; + } + + fseek(romfile, 0, SEEK_END); + GBAROMSize = ftell(romfile); + fseek(romfile, 0, SEEK_SET); + + sprintf(temp,"ROM Size %dMb (%dMBit)", GBAROMSize/1024/1024,(GBAROMSize*8)/1024/1024); + MENU_DrawString( -1, 150,temp , 1 ); + + rom = (u8 *)MEM2Storage; + + /* Always use MEM2, regardless of ROM size */ + res = gen_fread(rom, 1, GBAROMSize, romfile); + + if ( (u32)res != GBAROMSize ) + { + MENU_DrawString( -1, 400,"Error reading file!" , 1 ); + while(1); + } + strcpy( romfilename, filename ); + + CPUUpdateRenderBuffers( true ); + + return 1; +} + + +/**************************************************************************** +* VMRead32 +* +* Return a 32bit value +****************************************************************************/ +u32 VMRead32( u32 address ) +{ + u32 badaddress; + + if ( address >= GBAROMSize ) + { + badaddress = ( ( ( address >> 1 ) & 0xffff ) << 16 ) | ( ( ( address + 2 ) >> 1 ) & 0xffff ); + return badaddress; + } + + return READ32LE((rom + address)); + + +} + +/**************************************************************************** +* VMRead16 +* +* Return a 16bit value +****************************************************************************/ +u16 VMRead16( u32 address ) +{ + if ( address >= GBAROMSize ) + { + return ( address >> 1 ) & 0xffff; + } + + return READ16LE((rom + address)); + +} + +/**************************************************************************** +* VMRead8 +* +* Return 8bit value +****************************************************************************/ +u8 VMRead8( u32 address ) +{ + + if ( address >= GBAROMSize ) + { + return ( address >> 1 ) & 0xff; + } + + + return (u8)rom[address]; + +} +#endif +#ifdef GC_BUILD +/**************************************************************************** +* VisualBoyAdvance 1.7.2 +* +* GameBoy Advance Virtual Memory Paging +****************************************************************************/ +#include "sdfileio.h" +#include +#include +#include +#include + +#include "GBA.h" +#include "Globals.h" +#include "Util.h" +#include "Port.h" + +extern "C" { +#include "tbtime.h" +} + +/** Globals **/ +extern u32 loadtimeradjust; + +#define MEM_BAD 0xff +#define MEM_VM 0x01 +#define MEM_UN 0x80 + +/** Setup VM to use small 16kb windows **/ +#define VMSHIFTBITS 14 +#define VMSHIFTMASK 0x3FFF +#define MAXGBAROM ( 32 * 1024 * 1024 ) +#define MAXROM (4 * 1024 * 1024) +#define MAXVMPAGE ( MAXGBAROM >> VMSHIFTBITS ) +#define MAXVMMASK ( ( MAXROM >> VMSHIFTBITS ) - 1 ) + +typedef struct + { + char *pageptr; + int pagetype; + int pageno; + } +VMPAGE; + +static VMPAGE vmpage[MAXVMPAGE]; +static int vmpageno = 0; +static char *rombase = NULL; +static char *gbabase = NULL; +static FILE* romfile = NULL; +static int useVM = 0; +static u32 GBAROMSize = 0; +static char romfilename[1024]; + +/** + * GBA Memory + */ +#define WORKRAM 0x40000 +#define BIOS 0x4000 +#define INTERNALRAM 0x8000 +#define PALETTERAM 0x400 +#define VRAM 0x20000 +#define OAM 0x400 +#define PIX (4 * 241 * 162) +#define IOMEM 0x400 +#define GBATOTAL (WORKRAM + BIOS + INTERNALRAM + PALETTERAM + \ + VRAM + OAM + PIX + IOMEM ) + +extern void CPUUpdateRenderBuffers(bool force); + +/**************************************************************************** +* VMAllocGBA +* +* Allocate the memory required for GBA. +****************************************************************************/ +static void VMAllocGBA( void ) +{ + gbabase = (char *)memalign(32, GBATOTAL); + memset(gbabase, 0, GBATOTAL); + + /* Assign to internal GBA variables */ + workRAM = (u8 *)gbabase; + bios = (u8 *)(gbabase + WORKRAM); + internalRAM = (u8 *)(bios + BIOS); + paletteRAM = (u8 *)(internalRAM + INTERNALRAM); + vram = (u8 *)(paletteRAM + PALETTERAM); + oam = (u8 *)(vram + VRAM); + pix = (u8 *)(oam + OAM); + ioMem = (u8 *)(pix + PIX); +} + +/**************************************************************************** +* VMFindFree +* +* Look for a free page in the VM block. If none found, do a round-robin +****************************************************************************/ +static void VMFindFree( void ) +{ + int i; + + vmpageno++; + vmpageno &= MAXVMMASK; + if ( vmpageno == 0 ) vmpageno++; + + for ( i = 1; i < MAXVMPAGE; i++ ) + { + /** Remove any other pointer to this vmpage **/ + if ( vmpage[i].pageno == vmpageno ) + { + vmpage[i].pageptr = NULL; + vmpage[i].pagetype = MEM_UN; + vmpage[i].pageno = -1; + break; + } + } +} + +/**************************************************************************** +* VMAllocate +* +* Allocate a VM page +****************************************************************************/ +static void VMAllocate( int pageid ) +{ + VMFindFree(); + vmpage[pageid].pageptr = rombase + ( vmpageno << VMSHIFTBITS ); + vmpage[pageid].pagetype = MEM_VM; + vmpage[pageid].pageno = vmpageno; + +} + +/**************************************************************************** +* VMInit +* +* Set everything to default +****************************************************************************/ +static void VMInit( void ) +{ + int i; + + /** Clear down pointers **/ + memset(&vmpage, 0, sizeof(VMPAGE) * MAXVMPAGE); + for ( i = 0; i < MAXVMPAGE; i++ ) + { + vmpage[i].pageno = -1; + vmpage[i].pagetype = MEM_UN; + } + + /** Allocate physical **/ + if ( rombase == NULL ) + rombase = (char *)memalign(32, MAXROM); + + vmpageno = 0; + rom = (u8 *)rombase; +} + +/**************************************************************************** +* VMClose +****************************************************************************/ +static void VMClose( void ) +{ + if ( rombase != NULL ) + free(rombase); + + if ( gbabase != NULL ) + free(gbabase); + + if ( romfile != NULL ) + gen_fclose(romfile); + + rombase = gbabase = NULL; + romfile = NULL; + +} + +/**************************************************************************** +* VMCPULoadROM +* +* VM version of GBA CPULoadROM +****************************************************************************/ +int VMCPULoadROM( char *filename ) +{ + int res; + + /** Fix VM **/ + VMClose(); + VMInit(); + VMAllocGBA(); + + loadtimeradjust = useVM = GBAROMSize = 0; + + printf("Filename %s\n", filename); + + romfile = gen_fopen(filename, "rb"); + if ( romfile == NULL ) + { + printf("Error opening file!\n"); + while(1); + VMClose(); + return 0; + } + + // printf("ROM Size %d\n", romfile->fsize); + + /* Always use VM, regardless of ROM size */ + res = gen_fread(rom, 1, (1 << VMSHIFTBITS), romfile); + if ( res != (1 << VMSHIFTBITS ) ) + { + printf("Error reading file! %i \n",res); + while(1); + } + + fseek(romfile, 0, SEEK_END); + GBAROMSize = ftell(romfile); + fseek(romfile, 0, SEEK_SET); + vmpageno = 0; + vmpage[0].pageptr = rombase; + vmpage[0].pageno = 0; + vmpage[0].pagetype = MEM_VM; + useVM = 1; + + strcpy( romfilename, filename ); + + CPUUpdateRenderBuffers( true ); + + return 1; +} + +/**************************************************************************** +* GBA Memory Read Routines +****************************************************************************/ +/**************************************************************************** +* VMNewPage +****************************************************************************/ +static void VMNewPage( int pageid ) +{ + int res; + tb_t start,end; + + mftb(&start); + + res = gen_fseek( romfile, pageid << VMSHIFTBITS, SEEK_SET ); + if ( ! res ) + { + printf("Seek error! - Offset %08x %d\n", pageid << VMSHIFTBITS, res); + while(1); + } + + VMAllocate( pageid ); + + res = gen_fread( vmpage[pageid].pageptr, 1, 1 << VMSHIFTBITS, romfile ); + if ( res != ( 1 << VMSHIFTBITS ) ) + { + printf("Error reading! %d bytes only\n", res); + while(1); + } + + mftb(&end); + + loadtimeradjust += tb_diff_msec(&end, &start); + +#if 0 + if ( pageid == 0x1FE ) + { + vmpage[pageid].pageptr[0x209C] = 0xDF; + vmpage[pageid].pageptr[0x209D] = 0xFA; + vmpage[pageid].pageptr[0x209E] = 0x47; + vmpage[pageid].pageptr[0x209F] = 0x70; + } + + printf("VMNP : %02x %04x %08x [%02x%02x%02x%02x] [%02x%02x%02x%02x]\n", vmpageno, pageid, + (u32)(vmpage[pageid].pageptr - rombase), vmpage[pageid].pageptr[0], vmpage[pageid].pageptr[1], + vmpage[pageid].pageptr[2], vmpage[pageid].pageptr[3], + vmpage[pageid].pageptr[0xfffc], vmpage[pageid].pageptr[0xfffd], + vmpage[pageid].pageptr[0xfffe], vmpage[pageid].pageptr[0xffff] ); +#endif + +} + +/**************************************************************************** +* VMRead32 +* +* Return a 32bit value +****************************************************************************/ +u32 VMRead32( u32 address ) +{ + int pageid; + u32 badaddress; + //printf("VM32 : Request %08x\n", address); + + if ( address >= GBAROMSize ) + { + badaddress = ( ( ( address >> 1 ) & 0xffff ) << 16 ) | ( ( ( address + 2 ) >> 1 ) & 0xffff ); + return badaddress; + } + + if ( !useVM ) + return READ32LE((rom + address)); + + /** Use VM **/ + pageid = address >> VMSHIFTBITS; + + switch( vmpage[pageid].pagetype ) + { + case MEM_UN: + VMNewPage(pageid); + + case MEM_VM: + return READ32LE( vmpage[pageid].pageptr + ( address & VMSHIFTMASK ) ); + + default: + printf("VM32 : Unknown page type! (%d) [%d]\n", vmpage[pageid].pagetype, pageid); + while(1); + } + + /* Can never get here ... but stops gcc bitchin' */ + return 0; + +} + +/**************************************************************************** +* VMRead16 +* +* Return a 16bit value +****************************************************************************/ +u16 VMRead16( u32 address ) +{ + int pageid; + + //printf("VM16 : Request %08x\n", address); + + if ( address >= GBAROMSize ) + { + return ( address >> 1 ) & 0xffff; + } + + if ( !useVM ) + return READ16LE((rom + address)); + + pageid = address >> VMSHIFTBITS; + + switch( vmpage[pageid].pagetype ) + { + case MEM_UN: + VMNewPage(pageid); + + case MEM_VM: + return READ16LE( vmpage[pageid].pageptr + ( address & VMSHIFTMASK ) ); + + default: + printf("VM16 : Unknown page type!\n"); + while(1); + } + + /* Can never get here ... but stops gcc bitchin' */ + return 0; + +} + +/**************************************************************************** +* VMRead8 +* +* Return 8bit value +****************************************************************************/ +u8 VMRead8( u32 address ) +{ + int pageid; + + //printf("VM8 : Request %08x\n", address); + + if ( address >= GBAROMSize ) + { + return ( address >> 1 ) & 0xff; + } + + if ( !useVM ) + return (u8)rom[address]; + + pageid = address >> VMSHIFTBITS; + + switch( vmpage[pageid].pagetype ) + { + case MEM_UN: + VMNewPage(pageid); + + case MEM_VM: + return (u8)vmpage[pageid].pageptr[ (address & VMSHIFTMASK) ]; + + default: + printf("VM8 : Unknown page type!\n"); + while(1); + } + + /* Can never get here ... but stops gcc bitchin' */ + return 0; + +} + +#endif + diff --git a/src/ngc/vmmem.h b/src/ngc/vmmem.h new file mode 100644 index 0000000..829a5d1 --- /dev/null +++ b/src/ngc/vmmem.h @@ -0,0 +1,15 @@ +/**************************************************************************** +* VisualBoyAdvance 1.7.2 +* +* GameBoy Advance Virtual Memory Paging +****************************************************************************/ +#ifndef __VBAVMHDR__ +#define __VBAVMHDR__ + +int VMCPULoadROM( char *filename ); +u32 VMRead32( u32 address ); +u16 VMRead16( u32 address ); +u8 VMRead8( u32 address ); + +#endif + diff --git a/src/pixel.cpp b/src/pixel.cpp new file mode 100644 index 0000000..cfa7c7c --- /dev/null +++ b/src/pixel.cpp @@ -0,0 +1,157 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "System.h" + +extern int RGB_LOW_BITS_MASK; + +void Pixelate(u8 *srcPtr, u32 srcPitch, u8 *deltaPtr, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~(RGB_LOW_BITS_MASK | (RGB_LOW_BITS_MASK << 16)); + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + u32 *xP = (u32 *) deltaPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + u32 currentDelta; + u32 nextDelta; + + finish = (u8 *) bP + ((width+2) << 1); + nextPixel = *bP++; + nextDelta = *xP++; + + do + { + currentPixel = nextPixel; + currentDelta = nextDelta; + nextPixel = *bP++; + nextDelta = *xP++; + + if ((nextPixel != nextDelta) || (currentPixel != currentDelta)) + { + u32 colorA, colorB, product; + + *(xP - 2) = currentPixel; +#ifdef WORDS_BIGENDIAN + colorA = currentPixel >> 16; + colorB = currentPixel & 0xffff; +#else + colorA = currentPixel & 0xffff; + colorB = currentPixel >> 16; +#endif + product = (((colorA & colorMask) >> 1) & colorMask) >> 1; + +#ifdef WORDS_BIGENDIAN + *(nL) = (product << 16) | (product); + *(dP) = (colorA << 16) | product; +#else + *(nL) = product | (product << 16); + *(dP) = colorA | (product << 16); +#endif + +#ifdef WORDS_BIGENDIAN + colorA = nextPixel >> 16; +#else + colorA = nextPixel & 0xffff; +#endif + product = (((colorB & colorMask) >> 1) & colorMask) >> 1; +#ifdef WORDS_BIGENDIAN + *(nL + 1) = (product << 16) | (product); + *(dP + 1) = (colorB << 16) | (product); +#else + *(nL + 1) = (product) | (product << 16); + *(dP + 1) = (colorB) | (product << 16); +#endif + } + + dP += 2; + nL += 2; + } + while ((u8 *) bP < finish); + + deltaPtr += srcPitch; + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void Pixelate32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~RGB_LOW_BITS_MASK; + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + // u32 *xP = (u32 *) deltaPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+1) << 2); + nextPixel = *bP++; + + do + { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB, product; + + colorA = currentPixel; + colorB = nextPixel; + + product = (((colorA & colorMask) >> 1) & colorMask) >> 1; + *(nL) = product; + *(nL+1) = product; + *(dP) = colorA; + *(dP+1) = product; + + nextPixel = *bP++; + colorA = nextPixel; + product = (((colorB & colorMask) >> 1) & colorMask) >> 1; + *(nL + 2) = product; + *(nL + 3) = product; + *(dP + 2) = colorB; + *(dP + 3) = product; + + dP += 4; + nL += 4; + } + while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} diff --git a/src/scanline.cpp b/src/scanline.cpp new file mode 100644 index 0000000..a3d2e16 --- /dev/null +++ b/src/scanline.cpp @@ -0,0 +1,242 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "System.h" + +extern int RGB_LOW_BITS_MASK; + +void Scanlines (u8 *srcPtr, u32 srcPitch, u8 *, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+2) << 1); + nextPixel = *bP++; + + do + { + currentPixel = nextPixel; + nextPixel = *bP++; + u32 colorA, colorB; + +#ifdef WORDS_BIGENDIAN + colorA = currentPixel >> 16; + colorB = currentPixel & 0xffff; +#else + colorA = currentPixel & 0xffff; + colorB = currentPixel >> 16; +#endif + + *(dP) = colorA | colorA<<16; + *(nL) = 0; + +#ifdef WORDS_BIGENDIAN + colorA = nextPixel >> 16; +#else + colorA = nextPixel & 0xffff; +#endif + + *(dP + 1) = colorB | (colorB << 16); + *(nL + 1) = 0; + + dP += 2; + nL += 2; + } + while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void Scanlines32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+1) << 2); + nextPixel = *bP++; + + do + { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB; + + colorA = currentPixel; + colorB = nextPixel; + + *(dP) = colorA; + *(dP+1) = colorA; + *(nL) = 0; + *(nL+1) = 0; + + *(dP + 2) = colorB; + *(dP + 3) = colorB; + *(nL+2) = 0; + *(nL+3) = 0; + + nextPixel = *bP++; + + dP += 4; + nL += 4; + } + while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void ScanlinesTV(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~(RGB_LOW_BITS_MASK | (RGB_LOW_BITS_MASK << 16)); + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+2) << 1); + nextPixel = *bP++; + + do + { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB; + +#ifdef WORDS_BIGENDIAN + colorA = currentPixel >> 16; + colorB = currentPixel & 0xFFFF; +#else + colorA = currentPixel & 0xFFFF; + colorB = currentPixel >> 16; +#endif + + *(dP) = colorA = colorA | ((((colorA & colorMask) >> 1) + + ((colorB & colorMask) >> 1))) << 16; + colorA = ((colorA & colorMask) >> 1); + colorA += ((colorA & colorMask) >> 1); + *(nL) = colorA; + + colorA = nextPixel & 0xFFFF; + + *(dP + 1) = colorB = colorB | ((((colorA & colorMask) >> 1) + + ((colorB & colorMask) >> 1))) << 16; + colorB = ((colorB & colorMask) >> 1); + colorB += ((colorB & colorMask) >> 1); + + *(nL + 1) = colorB; + + dP += 2; + nL += 2; + } + while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void ScanlinesTV32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + u32 colorMask = ~RGB_LOW_BITS_MASK; + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + u32 nextPixel; + + finish = (u8 *) bP + ((width+1) << 2); + nextPixel = *bP++; + + do + { + currentPixel = nextPixel; + nextPixel = *bP++; + + u32 colorA, colorB, temp; + + colorA = currentPixel; + colorB = nextPixel; + + *(dP) = colorA; + *(dP+1) = temp = ((colorA & colorMask) >> 1) + + ((colorB & colorMask) >> 1); + temp = ((temp & colorMask) >> 1); + temp += ((temp & colorMask) >> 1); + colorA = ((colorA & colorMask) >> 1); + colorA += ((colorA & colorMask) >> 1); + + *(nL) = colorA; + *(nL+1) = temp; + + dP += 2; + nL += 2; + } + while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} diff --git a/src/simple2x.cpp b/src/simple2x.cpp new file mode 100644 index 0000000..9297a5c --- /dev/null +++ b/src/simple2x.cpp @@ -0,0 +1,112 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "System.h" + +void Simple2x(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + + finish = (u8 *) bP + ((width+2) << 1); + currentPixel = *bP++; + + do + { +#ifdef WORDS_BIGENDIAN + u32 color = currentPixel >> 16; +#else + u32 color = currentPixel & 0xffff; +#endif + + color = color | (color << 16); + + *(dP) = color; + *(nL) = color; + +#ifdef WORDS_BIGENDIAN + color = currentPixel & 0xffff; +#else + color = currentPixel >> 16; +#endif + color = color| (color << 16); + *(dP + 1) = color; + *(nL + 1) = color; + + currentPixel = *bP++; + + dP += 2; + nL += 2; + } + while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} + +void Simple2x32(u8 *srcPtr, u32 srcPitch, u8 * /* deltaPtr */, + u8 *dstPtr, u32 dstPitch, int width, int height) +{ + u8 *nextLine, *finish; + + nextLine = dstPtr + dstPitch; + + do + { + u32 *bP = (u32 *) srcPtr; + u32 *dP = (u32 *) dstPtr; + u32 *nL = (u32 *) nextLine; + u32 currentPixel; + + finish = (u8 *) bP + ((width+1) << 2); + currentPixel = *bP++; + + do + { + u32 color = currentPixel; + + *(dP) = color; + *(dP+1) = color; + *(nL) = color; + *(nL + 1) = color; + + currentPixel = *bP++; + + dP += 2; + nL += 2; + } + while ((u8 *) bP < finish); + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + nextLine += dstPitch << 1; + } + while (--height); +} diff --git a/src/thumb.h b/src/thumb.h new file mode 100644 index 0000000..d4b2830 --- /dev/null +++ b/src/thumb.h @@ -0,0 +1,2474 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifdef C_CORE +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) +#define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) |\ + (NEG(a) & POS(c)) |\ + (NEG(b) & POS(c))) ? true : false; +#define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) |\ + (NEG(a) & POS(c)) |\ + (POS(b) & POS(c))) ? true : false; +#define SUBOVERFLOW(a, b, c)\ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#define ADD_RD_RS_RN \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#define ADD_RD_RS_O3 \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#define ADD_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs + rhs;\ + reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#define CMN_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#define ADC_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs + (u32)C_FLAG;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#define SUB_RD_RS_RN \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define SUB_RD_RS_O3 \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define SUB_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define CMP_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define SBC_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs - !((u32)C_FLAG);\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#define LSL_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (32 - shift)) & 1 ? true : false;\ + value = reg[source].I << shift;\ + } +#define LSL_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (32 - value)) & 1 ? true : false;\ + value = reg[dest].I << value;\ + } +#define LSR_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (shift - 1)) & 1 ? true : false;\ + value = reg[source].I >> shift;\ + } +#define LSR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = reg[dest].I >> value;\ + } +#define ASR_RD_RM_I5 \ + {\ + C_FLAG = ((s32)reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)reg[source].I >> (int)shift;\ + } +#define ASR_RD_RS \ + {\ + C_FLAG = ((s32)reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\ + value = (s32)reg[dest].I >> (int)value;\ + } +#define ROR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = ((reg[dest].I << (32 - value)) |\ + (reg[dest].I >> value));\ + } +#define NEG_RD_RS \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = 0;\ + u32 res = rhs - lhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(rhs, lhs, res);\ + SUBOVERFLOW(rhs, lhs, res);\ + } +#define CMP_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#else +#ifdef __GNUC__ +#ifdef __POWERPC__ + #define ADD_RD_RS_RN \ + { \ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADD_RD_RS_O3 ADD_RD_RS_RN + #define ADD_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMN_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADC_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_RN \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_O3 SUB_RD_RS_RN + #define SUB_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SBC_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define LSL_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (32 - shift)) & 1 ? true : false;\ + value = reg[source].I << shift;\ + } + #define LSL_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (32 - value)) & 1 ? true : false;\ + value = reg[dest].I << value;\ + } + #define LSR_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (shift - 1)) & 1 ? true : false;\ + value = reg[source].I >> shift;\ + } + #define LSR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = reg[dest].I >> value;\ + } + #define ASR_RD_RM_I5 \ + {\ + C_FLAG = ((s32)reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)reg[source].I >> (int)shift;\ + } + #define ASR_RD_RS \ + {\ + C_FLAG = ((s32)reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\ + value = (s32)reg[dest].I >> (int)value;\ + } + #define ROR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = ((reg[dest].I << (32 - value)) |\ + (reg[dest].I >> value));\ + } + #define NEG_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (0) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#else +#define ADD_RD_RS_RN \ + asm ("add %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[source].I)); +#define ADD_RD_RS_O3 \ + asm ("add %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[source].I)); +#define ADD_RN_O8(d) \ + asm ("add %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[(d)].I)\ + : "r" (opcode & 255), "b" (reg[(d)].I)); +#define CMN_RD_RS \ + asm ("add %0, %1;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : \ + : "r" (value), "r" (reg[dest].I):"1"); +#define ADC_RD_RS \ + asm ("bt $0, C_FLAG;"\ + "adc %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setcb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[dest].I)); +#define SUB_RD_RS_RN \ + asm ("sub %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[source].I)); +#define SUB_RD_RS_O3 \ + asm ("sub %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[source].I)); +#define SUB_RN_O8(d) \ + asm ("sub %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[(d)].I)\ + : "r" (opcode & 255), "b" (reg[(d)].I)); +#define CMP_RN_O8(d) \ + asm ("sub %0, %1;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : \ + : "r" (opcode & 255), "r" (reg[(d)].I) : "1"); +#define SBC_RD_RS \ + asm volatile ("bt $0, C_FLAG;"\ + "cmc;"\ + "sbb %1, %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[dest].I) : "cc", "memory"); +#define LSL_RD_RM_I5 \ + asm ("shl %%cl, %%eax;"\ + "setcb C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[source].I), "c" (shift)); +#define LSL_RD_RS \ + asm ("shl %%cl, %%eax;"\ + "setcb C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); +#define LSR_RD_RM_I5 \ + asm ("shr %%cl, %%eax;"\ + "setcb C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[source].I), "c" (shift)); +#define LSR_RD_RS \ + asm ("shr %%cl, %%eax;"\ + "setcb C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); +#define ASR_RD_RM_I5 \ + asm ("sar %%cl, %%eax;"\ + "setcb C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[source].I), "c" (shift)); +#define ASR_RD_RS \ + asm ("sar %%cl, %%eax;"\ + "setcb C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); +#define ROR_RD_RS \ + asm ("ror %%cl, %%eax;"\ + "setcb C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); +#define NEG_RD_RS \ + asm ("neg %%ebx;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "b" (reg[source].I)); +#define CMP_RD_RS \ + asm ("sub %0, %1;"\ + "setsb N_FLAG;"\ + "setzb Z_FLAG;"\ + "setncb C_FLAG;"\ + "setob V_FLAG;"\ + : \ + : "r" (value), "r" (reg[dest].I):"1"); +#endif +#else +#define ADD_RD_RS_RN \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define ADD_RD_RS_O3 \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define ADD_RN_O8(d) \ + {\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm add dword ptr [OFFSET reg+4*(d)], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define CMN_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define ADC_RD_RS \ + {\ + __asm mov ebx, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm bt word ptr C_FLAG, 0\ + __asm adc ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define SUB_RD_RS_RN \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define SUB_RD_RS_O3 \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define SUB_RN_O8(d) \ + {\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm sub dword ptr [OFFSET reg + 4*(d)], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define CMP_RN_O8(d) \ + {\ + __asm mov eax, dword ptr [OFFSET reg+4*(d)]\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm sub eax, ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define SBC_RD_RS \ + {\ + __asm mov ebx, dest\ + __asm mov ebx, dword ptr [OFFSET reg + 4*ebx]\ + __asm mov eax, value\ + __asm bt word ptr C_FLAG, 0\ + __asm cmc\ + __asm sbb ebx, eax\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg + 4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define LSL_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shl eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } +#define LSL_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr value\ + __asm shl eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } +#define LSR_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shr eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } +#define LSR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr value\ + __asm shr eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } +#define ASR_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr shift\ + __asm sar eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } +#define ASR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr value\ + __asm sar eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } +#define ROR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr value\ + __asm ror eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } +#define NEG_RD_RS \ + {\ + __asm mov ebx, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm neg ebx\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax],ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#define CMP_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#endif +#endif + +u32 opcode = CPUReadHalfWordQuick(armNextPC); +clockTicks = thumbCycles[opcode >> 8] + memoryWaitFetch[(armNextPC >> 24) & 15]; +#ifndef FINAL_VERSION +if(armNextPC == stop) { + armNextPC = armNextPC++; +} +#endif + +armNextPC = reg[15].I; +reg[15].I += 2; + +switch(opcode >> 8) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + { + // LSL Rd, Rm, #Imm 5 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + int shift = (opcode >> 6) & 0x1f; + u32 value; + + if(shift) { + LSL_RD_RM_I5; + } else { + value = reg[source].I; + } + reg[dest].I = value; + // C_FLAG set above + N_FLAG = (value & 0x80000000 ? true : false); + Z_FLAG = (value ? false : true); + } + break; + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + { + // LSR Rd, Rm, #Imm 5 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + int shift = (opcode >> 6) & 0x1f; + u32 value; + + if(shift) { + LSR_RD_RM_I5; + } else { + C_FLAG = reg[source].I & 0x80000000 ? true : false; + value = 0; + } + reg[dest].I = value; + // C_FLAG set above + N_FLAG = (value & 0x80000000 ? true : false); + Z_FLAG = (value ? false : true); + } + break; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + { + // ASR Rd, Rm, #Imm 5 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + int shift = (opcode >> 6) & 0x1f; + u32 value; + + if(shift) { + ASR_RD_RM_I5; + } else { + if(reg[source].I & 0x80000000) { + value = 0xFFFFFFFF; + C_FLAG = true; + } else { + value = 0; + C_FLAG = false; + } + } + reg[dest].I = value; + // C_FLAG set above + N_FLAG = (value & 0x80000000 ? true : false); + Z_FLAG = (value ? false :true); + } + break; + case 0x18: + case 0x19: + { + // ADD Rd, Rs, Rn + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = reg[(opcode>>6)& 0x07].I; + ADD_RD_RS_RN; + } + break; + case 0x1a: + case 0x1b: + { + // SUB Rd, Rs, Rn + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = reg[(opcode>>6)& 0x07].I; + SUB_RD_RS_RN; + } + break; + case 0x1c: + case 0x1d: + { + // ADD Rd, Rs, #Offset3 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = (opcode >> 6) & 7; + ADD_RD_RS_O3; + } + break; + case 0x1e: + case 0x1f: + { + // SUB Rd, Rs, #Offset3 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = (opcode >> 6) & 7; + SUB_RD_RS_O3; + } + break; + case 0x20: + // MOV R0, #Offset8 + reg[0].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[0].I ? false : true); + break; + case 0x21: + // MOV R1, #Offset8 + reg[1].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[1].I ? false : true); + break; + case 0x22: + // MOV R2, #Offset8 + reg[2].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[2].I ? false : true); + break; + case 0x23: + // MOV R3, #Offset8 + reg[3].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[3].I ? false : true); + break; + case 0x24: + // MOV R4, #Offset8 + reg[4].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[4].I ? false : true); + break; + case 0x25: + // MOV R5, #Offset8 + reg[5].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[5].I ? false : true); + break; + case 0x26: + // MOV R6, #Offset8 + reg[6].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[6].I ? false : true); + break; + case 0x27: + // MOV R7, #Offset8 + reg[7].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[7].I ? false : true); + break; + case 0x28: + // CMP R0, #Offset8 + CMP_RN_O8(0); + break; + case 0x29: + // CMP R1, #Offset8 + CMP_RN_O8(1); + break; + case 0x2a: + // CMP R2, #Offset8 + CMP_RN_O8(2); + break; + case 0x2b: + // CMP R3, #Offset8 + CMP_RN_O8(3); + break; + case 0x2c: + // CMP R4, #Offset8 + CMP_RN_O8(4); + break; + case 0x2d: + // CMP R5, #Offset8 + CMP_RN_O8(5); + break; + case 0x2e: + // CMP R6, #Offset8 + CMP_RN_O8(6); + break; + case 0x2f: + // CMP R7, #Offset8 + CMP_RN_O8(7); + break; + case 0x30: + // ADD R0,#Offset8 + ADD_RN_O8(0); + break; + case 0x31: + // ADD R1,#Offset8 + ADD_RN_O8(1); + break; + case 0x32: + // ADD R2,#Offset8 + ADD_RN_O8(2); + break; + case 0x33: + // ADD R3,#Offset8 + ADD_RN_O8(3); + break; + case 0x34: + // ADD R4,#Offset8 + ADD_RN_O8(4); + break; + case 0x35: + // ADD R5,#Offset8 + ADD_RN_O8(5); + break; + case 0x36: + // ADD R6,#Offset8 + ADD_RN_O8(6); + break; + case 0x37: + // ADD R7,#Offset8 + ADD_RN_O8(7); + break; + case 0x38: + // SUB R0,#Offset8 + SUB_RN_O8(0); + break; + case 0x39: + // SUB R1,#Offset8 + SUB_RN_O8(1); + break; + case 0x3a: + // SUB R2,#Offset8 + SUB_RN_O8(2); + break; + case 0x3b: + // SUB R3,#Offset8 + SUB_RN_O8(3); + break; + case 0x3c: + // SUB R4,#Offset8 + SUB_RN_O8(4); + break; + case 0x3d: + // SUB R5,#Offset8 + SUB_RN_O8(5); + break; + case 0x3e: + // SUB R6,#Offset8 + SUB_RN_O8(6); + break; + case 0x3f: + // SUB R7,#Offset8 + SUB_RN_O8(7); + break; + case 0x40: + switch((opcode >> 6) & 3) { + case 0x00: + { + // AND Rd, Rs + int dest = opcode & 7; + reg[dest].I &= reg[(opcode >> 3)&7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; +#ifdef BKPT_SUPPORT +#define THUMB_CONSOLE_OUTPUT(a,b) \ + if((opcode == 0x4000) && (reg[0].I == 0xC0DED00D)) {\ + extern void (*dbgOutput)(char *, u32);\ + dbgOutput((a), (b));\ + } +#else +#define THUMB_CONSOLE_OUTPUT(a,b) +#endif + THUMB_CONSOLE_OUTPUT(NULL, reg[2].I); + } + break; + case 0x01: + // EOR Rd, Rs + { + int dest = opcode & 7; + reg[dest].I ^= reg[(opcode >> 3)&7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + } + break; + case 0x02: + // LSL Rd, Rs + { + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value == 32) { + value = 0; + C_FLAG = (reg[dest].I & 1 ? true : false); + } else if(value < 32) { + LSL_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks++; + } + break; + case 0x03: + { + // LSR Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value == 32) { + value = 0; + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } else if(value < 32) { + LSR_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks++; + } + break; + } + break; + case 0x41: + switch((opcode >> 6) & 3) { + case 0x00: + { + // ASR Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + // ASR + if(value) { + if(value < 32) { + ASR_RD_RS; + reg[dest].I = value; + } else { + if(reg[dest].I & 0x80000000){ + reg[dest].I = 0xFFFFFFFF; + C_FLAG = true; + } else { + reg[dest].I = 0x00000000; + C_FLAG = false; + } + } + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks++; + } + break; + case 0x01: + { + // ADC Rd, Rs + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3)&7].I; + // ADC + ADC_RD_RS; + } + break; + case 0x02: + { + // SBC Rd, Rs + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3)&7].I; + + // SBC + SBC_RD_RS; + } + break; + case 0x03: + // ROR Rd, Rs + { + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + + if(value) { + value = value & 0x1f; + if(value == 0) { + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } else { + ROR_RD_RS; + reg[dest].I = value; + } + } + clockTicks++; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + } + break; + } + break; + case 0x42: + switch((opcode >> 6) & 3) { + case 0x00: + { + // TST Rd, Rs + u32 value = reg[opcode & 7].I & reg[(opcode >> 3) & 7].I; + N_FLAG = value & 0x80000000 ? true : false; + Z_FLAG = value ? false : true; + } + break; + case 0x01: + { + // NEG Rd, Rs + int dest = opcode & 7; + int source = (opcode >> 3) & 7; + NEG_RD_RS; + } + break; + case 0x02: + { + // CMP Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].I; + CMP_RD_RS; + } + break; + case 0x03: + { + // CMN Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].I; + // CMN + CMN_RD_RS; + } + break; + } + break; + case 0x43: + switch((opcode >> 6) & 3) { + case 0x00: + { + // ORR Rd, Rs + int dest = opcode & 7; + reg[dest].I |= reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + case 0x01: + { + // MUL Rd, Rs + int dest = opcode & 7; + u32 rm = reg[(opcode >> 3) & 7].I; + reg[dest].I = reg[dest].I * rm; + if (((s32)rm) < 0) + rm = ~rm; + if ((rm & 0xFFFFFF00) == 0) + clockTicks += 1; + else if ((rm & 0xFFFF0000) == 0) + clockTicks += 2; + else if ((rm & 0xFF000000) == 0) + clockTicks += 3; + else + clockTicks += 4; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + case 0x02: + { + // BIC Rd, Rs + int dest = opcode & 7; + reg[dest].I &= (~reg[(opcode >> 3) & 7].I); + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + case 0x03: + { + // MVN Rd, Rs + int dest = opcode & 7; + reg[dest].I = ~reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + } + break; + case 0x44: + { + int dest = opcode & 7; + int base = (opcode >> 3) & 7; + switch((opcode >> 6)& 3) { + default: + goto unknown_thumb; + case 1: + // ADD Rd, Hs + reg[dest].I += reg[base+8].I; + break; + case 2: + // ADD Hd, Rs + reg[dest+8].I += reg[base].I; + if(dest == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + case 3: + // ADD Hd, Hs + reg[dest+8].I += reg[base+8].I; + if(dest == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + } + } + break; + case 0x45: + { + int dest = opcode & 7; + int base = (opcode >> 3) & 7; + u32 value; + switch((opcode >> 6) & 3) { + case 0: + // CMP Rd, Hs + value = reg[base].I; + CMP_RD_RS; + break; + case 1: + // CMP Rd, Hs + value = reg[base+8].I; + CMP_RD_RS; + break; + case 2: + // CMP Hd, Rs + value = reg[base].I; + dest += 8; + CMP_RD_RS; + break; + case 3: + // CMP Hd, Hs + value = reg[base+8].I; + dest += 8; + CMP_RD_RS; + break; + } + } + break; + case 0x46: + { + int dest = opcode & 7; + int base = (opcode >> 3) & 7; + switch((opcode >> 6) & 3) { + case 0: + // this form should not be used... + // MOV Rd, Rs + reg[dest].I = reg[base].I; + break; + case 1: + // MOV Rd, Hs + reg[dest].I = reg[base+8].I; + break; + case 2: + // MOV Hd, Rs + reg[dest+8].I = reg[base].I; + if(dest == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + case 3: + // MOV Hd, Hs + reg[dest+8].I = reg[base+8].I; + if(dest == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + } + } + break; + case 0x47: + { + int base = (opcode >> 3) & 7; + switch((opcode >>6) & 3) { + case 0: + // BX Rs + reg[15].I = (reg[base].I) & 0xFFFFFFFE; + if(reg[base].I & 1) { + armState = false; + armNextPC = reg[15].I; + reg[15].I += 2; + } else { + armState = true; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + case 1: + // BX Hs + reg[15].I = (reg[8+base].I) & 0xFFFFFFFE; + if(reg[8+base].I & 1) { + armState = false; + armNextPC = reg[15].I; + reg[15].I += 2; + } else { + armState = true; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + default: + goto unknown_thumb; + } + } + break; + case 0x48: + // LDR R0,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[0].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x49: + // LDR R1,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[1].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x4a: + // LDR R2,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[2].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x4b: + // LDR R3,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[3].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x4c: + // LDR R4,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[4].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x4d: + // LDR R5,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[5].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x4e: + // LDR R6,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[6].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x4f: + // LDR R7,[PC, #Imm] + { + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[7].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x50: + case 0x51: + // STR Rd, [Rs, Rn] + { + u32 + address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + CPUWriteMemory(address, + reg[opcode & 7].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x52: + case 0x53: + // STRH Rd, [Rs, Rn] + { + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + CPUWriteHalfWord(address, + reg[opcode&7].W.W0); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x54: + case 0x55: + // STRB Rd, [Rs, Rn] + { + u32 address = reg[(opcode>>3)&7].I + reg[(opcode >>6)&7].I; + CPUWriteByte(address, + reg[opcode & 7].B.B0); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x56: + case 0x57: + // LDSB Rd, [Rs, Rn] + { + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = (s8)CPUReadByte(address); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x58: + case 0x59: + // LDR Rd, [Rs, Rn] + { + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadMemory(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x5a: + case 0x5b: + // LDRH Rd, [Rs, Rn] + { + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadHalfWord(address); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x5c: + case 0x5d: + // LDRB Rd, [Rs, Rn] + { + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadByte(address); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x5e: + case 0x5f: + // LDSH Rd, [Rs, Rn] + { + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = (s16)CPUReadHalfWordSigned(address); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + // STR Rd, [Rs, #Imm] + { + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + CPUWriteMemory(address, + reg[opcode&7].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + // LDR Rd, [Rs, #Imm] + { + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + reg[opcode&7].I = CPUReadMemory(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + // STRB Rd, [Rs, #Imm] + { + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + CPUWriteByte(address, + reg[opcode&7].B.B0); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x78: + case 0x79: + case 0x7a: + case 0x7b: + case 0x7c: + case 0x7d: + case 0x7e: + case 0x7f: + // LDRB Rd, [Rs, #Imm] + { + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + reg[opcode&7].I = CPUReadByte(address); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + // STRH Rd, [Rs, #Imm] + { + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + CPUWriteHalfWord(address, + reg[opcode&7].W.W0); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + // LDRH Rd, [Rs, #Imm] + { + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + reg[opcode&7].I = CPUReadHalfWord(address); + clockTicks += CPUUpdateTicksAccess16(address); + } + break; + case 0x90: + // STR R0, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[0].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x91: + // STR R1, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[1].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x92: + // STR R2, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[2].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x93: + // STR R3, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[3].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x94: + // STR R4, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[4].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x95: + // STR R5, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[5].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x96: + // STR R6, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[6].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x97: + // STR R7, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[7].I); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x98: + // LDR R0, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[0].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x99: + // LDR R1, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[1].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x9a: + // LDR R2, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[2].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x9b: + // LDR R3, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[3].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x9c: + // LDR R4, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[4].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x9d: + // LDR R5, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[5].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x9e: + // LDR R6, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[6].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0x9f: + // LDR R7, [SP, #Imm] + { + u32 address = reg[13].I + ((opcode&255)<<2); + reg[7].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); + } + break; + case 0xa0: + // ADD R0, PC, Imm + reg[0].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa1: + // ADD R1, PC, Imm + reg[1].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa2: + // ADD R2, PC, Imm + reg[2].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa3: + // ADD R3, PC, Imm + reg[3].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa4: + // ADD R4, PC, Imm + reg[4].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa5: + // ADD R5, PC, Imm + reg[5].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa6: + // ADD R6, PC, Imm + reg[6].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa7: + // ADD R7, PC, Imm + reg[7].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); + break; + case 0xa8: + // ADD R0, SP, Imm + reg[0].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xa9: + // ADD R1, SP, Imm + reg[1].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xaa: + // ADD R2, SP, Imm + reg[2].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xab: + // ADD R3, SP, Imm + reg[3].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xac: + // ADD R4, SP, Imm + reg[4].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xad: + // ADD R5, SP, Imm + reg[5].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xae: + // ADD R6, SP, Imm + reg[6].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xaf: + // ADD R7, SP, Imm + reg[7].I = reg[13].I + ((opcode&255)<<2); + break; + case 0xb0: + { + // ADD SP, Imm + int offset = (opcode & 127) << 2; + if(opcode & 0x80) + offset = -offset; + reg[13].I += offset; + } + break; +#define PUSH_REG(val, r) \ + if(opcode & (val)) {\ + CPUWriteMemory(address, reg[(r)].I);\ + if(offset)\ + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address);\ + else\ + clockTicks += 1 + CPUUpdateTicksAccess32(address);\ + offset = 1;\ + address += 4;\ + } + case 0xb4: + // PUSH {Rlist} + { + int offset = 0; + u32 temp = reg[13].I - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + reg[13].I = temp; + } + break; + case 0xb5: + // PUSH {Rlist, LR} + { + int offset = 0; + u32 temp = reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + PUSH_REG(256, 14); + reg[13].I = temp; + } + break; +#define POP_REG(val, r) \ + if(opcode & (val)) {\ + reg[(r)].I = CPUReadMemory(address);\ + if(offset)\ + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address);\ + else\ + clockTicks += 2 + CPUUpdateTicksAccess32(address);\ + offset = 1;\ + address += 4;\ + } + case 0xbc: + // POP {Rlist} + { + int offset = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[13].I = temp; + } + break; + case 0xbd: + // POP {Rlist, PC} + { + int offset = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4 + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE); + if(offset) + clockTicks += CPUUpdateTicksAccessSeq32(address); + else + clockTicks += CPUUpdateTicksAccess32(address); + armNextPC = reg[15].I; + reg[15].I += 2; + reg[13].I = temp; + } + break; +#define THUMB_STM_REG(val,r,b) \ + if(opcode & (val)) {\ + CPUWriteMemory(address, reg[(r)].I);\ + if(!offset) {\ + reg[(b)].I = temp;\ + clockTicks += 1 + CPUUpdateTicksAccess32(address);\ + } else \ + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address);\ + offset = 1;\ + address += 4;\ + } + case 0xc0: + { + // STM R0!, {Rlist} + u32 address = reg[0].I & 0xFFFFFFFC; + u32 temp = reg[0].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 0); + THUMB_STM_REG(2, 1, 0); + THUMB_STM_REG(4, 2, 0); + THUMB_STM_REG(8, 3, 0); + THUMB_STM_REG(16, 4, 0); + THUMB_STM_REG(32, 5, 0); + THUMB_STM_REG(64, 6, 0); + THUMB_STM_REG(128, 7, 0); + } + break; + case 0xc1: + { + // STM R1!, {Rlist} + u32 address = reg[1].I & 0xFFFFFFFC; + u32 temp = reg[1].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 1); + THUMB_STM_REG(2, 1, 1); + THUMB_STM_REG(4, 2, 1); + THUMB_STM_REG(8, 3, 1); + THUMB_STM_REG(16, 4, 1); + THUMB_STM_REG(32, 5, 1); + THUMB_STM_REG(64, 6, 1); + THUMB_STM_REG(128, 7, 1); + } + break; + case 0xc2: + { + // STM R2!, {Rlist} + u32 address = reg[2].I & 0xFFFFFFFC; + u32 temp = reg[2].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 2); + THUMB_STM_REG(2, 1, 2); + THUMB_STM_REG(4, 2, 2); + THUMB_STM_REG(8, 3, 2); + THUMB_STM_REG(16, 4, 2); + THUMB_STM_REG(32, 5, 2); + THUMB_STM_REG(64, 6, 2); + THUMB_STM_REG(128, 7, 2); + } + break; + case 0xc3: + { + // STM R3!, {Rlist} + u32 address = reg[3].I & 0xFFFFFFFC; + u32 temp = reg[3].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 3); + THUMB_STM_REG(2, 1, 3); + THUMB_STM_REG(4, 2, 3); + THUMB_STM_REG(8, 3, 3); + THUMB_STM_REG(16, 4, 3); + THUMB_STM_REG(32, 5, 3); + THUMB_STM_REG(64, 6, 3); + THUMB_STM_REG(128, 7, 3); + } + break; + case 0xc4: + { + // STM R4!, {Rlist} + u32 address = reg[4].I & 0xFFFFFFFC; + u32 temp = reg[4].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 4); + THUMB_STM_REG(2, 1, 4); + THUMB_STM_REG(4, 2, 4); + THUMB_STM_REG(8, 3, 4); + THUMB_STM_REG(16, 4, 4); + THUMB_STM_REG(32, 5, 4); + THUMB_STM_REG(64, 6, 4); + THUMB_STM_REG(128, 7, 4); + } + break; + case 0xc5: + { + // STM R5!, {Rlist} + u32 address = reg[5].I & 0xFFFFFFFC; + u32 temp = reg[5].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 5); + THUMB_STM_REG(2, 1, 5); + THUMB_STM_REG(4, 2, 5); + THUMB_STM_REG(8, 3, 5); + THUMB_STM_REG(16, 4, 5); + THUMB_STM_REG(32, 5, 5); + THUMB_STM_REG(64, 6, 5); + THUMB_STM_REG(128, 7, 5); + } + break; + case 0xc6: + { + // STM R6!, {Rlist} + u32 address = reg[6].I & 0xFFFFFFFC; + u32 temp = reg[6].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 6); + THUMB_STM_REG(2, 1, 6); + THUMB_STM_REG(4, 2, 6); + THUMB_STM_REG(8, 3, 6); + THUMB_STM_REG(16, 4, 6); + THUMB_STM_REG(32, 5, 6); + THUMB_STM_REG(64, 6, 6); + THUMB_STM_REG(128, 7, 6); + } + break; + case 0xc7: + { + // STM R7!, {Rlist} + u32 address = reg[7].I & 0xFFFFFFFC; + u32 temp = reg[7].I + 4*cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 7); + THUMB_STM_REG(2, 1, 7); + THUMB_STM_REG(4, 2, 7); + THUMB_STM_REG(8, 3, 7); + THUMB_STM_REG(16, 4, 7); + THUMB_STM_REG(32, 5, 7); + THUMB_STM_REG(64, 6, 7); + THUMB_STM_REG(128, 7, 7); + } + break; +#define THUMB_LDM_REG(val,r) \ + if(opcode & (val)) {\ + reg[(r)].I = CPUReadMemory(address);\ + if(offset)\ + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address);\ + else\ + clockTicks += 2 + CPUUpdateTicksAccess32(address);\ + offset = 1;\ + address += 4;\ + } + case 0xc8: + { + // LDM R0!, {Rlist} + u32 address = reg[0].I & 0xFFFFFFFC; + u32 temp = reg[0].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 1)) + reg[0].I = temp; + } + break; + case 0xc9: + { + // LDM R1!, {Rlist} + u32 address = reg[1].I & 0xFFFFFFFC; + u32 temp = reg[1].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 2)) + reg[1].I = temp; + } + break; + case 0xca: + { + // LDM R2!, {Rlist} + u32 address = reg[2].I & 0xFFFFFFFC; + u32 temp = reg[2].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 4)) + reg[2].I = temp; + } + break; + case 0xcb: + { + // LDM R3!, {Rlist} + u32 address = reg[3].I & 0xFFFFFFFC; + u32 temp = reg[3].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 8)) + reg[3].I = temp; + } + break; + case 0xcc: + { + // LDM R4!, {Rlist} + u32 address = reg[4].I & 0xFFFFFFFC; + u32 temp = reg[4].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 16)) + reg[4].I = temp; + } + break; + case 0xcd: + { + // LDM R5!, {Rlist} + u32 address = reg[5].I & 0xFFFFFFFC; + u32 temp = reg[5].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 32)) + reg[5].I = temp; + } + break; + case 0xce: + { + // LDM R6!, {Rlist} + u32 address = reg[6].I & 0xFFFFFFFC; + u32 temp = reg[6].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 64)) + reg[6].I = temp; + } + break; + case 0xcf: + { + // LDM R7!, {Rlist} + u32 address = reg[7].I & 0xFFFFFFFC; + u32 temp = reg[7].I + 4*cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if(!(opcode & 128)) + reg[7].I = temp; + } + break; + case 0xd0: + // BEQ offset + if(Z_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd1: + // BNE offset + if(!Z_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd2: + // BCS offset + if(C_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd3: + // BCC offset + if(!C_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd4: + // BMI offset + if(N_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd5: + // BPL offset + if(!N_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd6: + // BVS offset + if(V_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd7: + // BVC offset + if(!V_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd8: + // BHI offset + if(C_FLAG && !Z_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xd9: + // BLS offset + if(!C_FLAG || Z_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xda: + // BGE offset + if(N_FLAG == V_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xdb: + // BLT offset + if(N_FLAG != V_FLAG) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xdc: + // BGT offset + if(!Z_FLAG && (N_FLAG == V_FLAG)) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xdd: + // BLE offset + if(Z_FLAG || (N_FLAG != V_FLAG)) { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; + case 0xdf: + // SWI #comment + CPUSoftwareInterrupt(opcode & 0xFF); + break; + case 0xe0: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + { + // B offset + int offset = (opcode & 0x3FF) << 1; + if(opcode & 0x0400) + offset |= 0xFFFFF800; + reg[15].I += offset; + armNextPC = reg[15].I; + reg[15].I += 2; + } + break; + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + { + // BLL #offset + int offset = (opcode & 0x7FF); + reg[14].I = reg[15].I + (offset << 12); + } + break; + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + { + // BLL #offset + int offset = (opcode & 0x7FF); + reg[14].I = reg[15].I + ((offset << 12) | 0xFF800000); + } + break; + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + case 0xfc: + case 0xfd: + case 0xfe: + case 0xff: + { + // BLH #offset + int offset = (opcode & 0x7FF); + u32 temp = reg[15].I-2; + reg[15].I = (reg[14].I + (offset<<1))&0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + reg[14].I = temp|1; + } + break; +#ifdef BKPT_SUPPORT + case 0xbe: + // BKPT #comment + extern void (*dbgSignal)(int,int); + reg[15].I -= 2; + armNextPC -= 2; + dbgSignal(5, opcode & 255); + return; +#endif + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: +#ifndef BKPT_SUPPORT + case 0xbe: +#endif + case 0xbf: + case 0xde: + default: + unknown_thumb: +#ifdef DEV_VERSION + if(systemVerbose & VERBOSE_UNDEFINED) + log("Undefined THUMB instruction %04x at %08x\n", opcode, armNextPC-2); +#endif + CPUUndefinedException(); + break; +} diff --git a/src/unzip.cpp b/src/unzip.cpp new file mode 100644 index 0000000..0703d0c --- /dev/null +++ b/src/unzip.cpp @@ -0,0 +1,1277 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/* unzip.c -- IO on .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Read unzip.h for more info +*/ + +#include "sdfileio.h" +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H +extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +const char unz_copyright[] = + " unzip 0.15 Copyright 1998 Gilles Vollant "; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s + { + uLong offset_curfile;/* relative offset of local header 4 bytes */ + } +unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct + { + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + FILE* file; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + } +file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct + { + FILE* file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + } +unz_s; + + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte(FILE* fin,int *pi) +{ + unsigned char c; + int err = gen_fread(&c, 1, 1, fin); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { +#if 0 + if (ferror(fin)) + return UNZ_ERRNO; + else + return UNZ_EOF; +#else + return UNZ_EOF; +#endif + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort (FILE* fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong (FILE* fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char *fileName1, + const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char *fileName1, + const char *fileName2, + int iCaseSensitivity) + { + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); + } + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir(FILE* fin) +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + + if (gen_fseek(fin,0,SEEK_END) != 0) + return 0; + + + uSizeFile = ftell( fin ); + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (gen_fseek(fin,uReadPos,SEEK_SET)!=0) + break; + + if (gen_fread(buf,(uInt)uReadSize,1,fin)!=1) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen (const char *path) + { + unz_s us; + unz_s *s; + uLong central_pos,uL; + FILE* fin ; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + fin=gen_fopen(path,"rb"); + if (fin==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) + err=UNZ_ERRNO; + + if (gen_fseek(fin,central_pos,SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + gen_fclose(s->file); + TRYFREE(s); + return UNZ_OK; + } + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (unzFile file, + unz_global_info *pglobal_info) + { + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; + } + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz *ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (gen_fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (gen_fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (gen_fread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (gen_fread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) + { + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + } + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) + { + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; + } + + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) + { + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; + } + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, + const char *szFileName, + int iCaseSensitivity) + { + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; + } + + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s, + uInt *piSizeVar, + uLong *poffset_local_extrafield, + uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (gen_fseek(s->file,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + /* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; + */ + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile (unzFile file) + { + int err=UNZ_OK; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; + } + + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) + { + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (gen_fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + if (gen_fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, + pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { + uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; + } + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) + { + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; + } + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) + { + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; + } + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) + { + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (gen_fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (gen_fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; + } + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) + { + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; + } + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, + char *szComment, + uLong uSizeBuf) + { + //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (gen_fseek(s->file,s->central_pos+22,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (gen_fread(szComment,(uInt)uReadThis,1,s->file)!=1) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; + } diff --git a/src/unzip.h b/src/unzip.h new file mode 100644 index 0000000..97bdbe0 --- /dev/null +++ b/src/unzip.h @@ -0,0 +1,302 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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 2, or(at your option) +// any later version. +// +// 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 program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" + { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) + /* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ + typedef struct TagunzFile__ + { + int unused; + } + unzFile__; + typedef unzFile__ *unzFile; +#else + typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + /* tm_unz contain date/time info */ + typedef struct tm_unz_s + { + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ + } + tm_unz; + + /* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ + typedef struct unz_global_info_s + { + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ + } + unz_global_info; + + + /* unz_file_info contain information about a file in the zipfile */ + typedef struct unz_file_info_s + { + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; + } + unz_file_info; + + extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); + /* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + */ + + + extern unzFile ZEXPORT unzOpen OF((const char *path)); + /* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + */ + + extern int ZEXPORT unzClose OF((unzFile file)); + /* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + + extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + /* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + + extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); + /* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 + */ + + + /***************************************************************************/ + /* Unzip package allow you browse the directory of the zipfile */ + + extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); + /* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem + */ + + extern int ZEXPORT unzGoToNextFile OF((unzFile file)); + /* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. + */ + + extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); + /* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found + */ + + + extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + /* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) + */ + + /***************************************************************************/ + /* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + + extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); + /* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. + */ + + extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); + /* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good + */ + + + extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); + /* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) + */ + + extern z_off_t ZEXPORT unztell OF((unzFile file)); + /* + Give the current position in uncompressed data + */ + + extern int ZEXPORT unzeof OF((unzFile file)); + /* + return 1 if the end of file was reached, 0 elsewhere + */ + + extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); + /* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code + */ + +#ifdef __cplusplus + } +#endif + +#endif /* _unz_H */ diff --git a/src/zutil.h b/src/zutil.h new file mode 100644 index 0000000..4726517 --- /dev/null +++ b/src/zutil.h @@ -0,0 +1,220 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-1998 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.2 2004/01/17 23:07:32 kxu Exp $ */ + +#ifndef _Z_UTIL_H +#define _Z_UTIL_H + +#include + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H +extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + +/* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + +/* target dependencies */ + +#ifdef MSDOS +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) +/* Allow compilation with ANSI keywords only enabled */ +void _Cdecl farfree( void *block ); +void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#ifdef WIN32 /* Window 95 & Windows NT */ +# define OS_CODE 0x0b +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0F +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# define fdopen(fd,type) _fdopen(fd,type) +#endif + + +/* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + +/* functions */ + +#ifdef HAVE_STRERROR +extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) +/* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else +extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); +extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); +extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include +extern int z_verbose; +extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf, + uInt len)); +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* _Z_UTIL_H */