From 30f06884926d7362d50dd8551bbb823c5a52d2d2 Mon Sep 17 00:00:00 2001 From: wavemotion-dave <75039837+wavemotion-dave@users.noreply.github.com> Date: Thu, 2 Sep 2021 17:32:31 -0400 Subject: [PATCH] First commit of semi-working emulator! --- Makefile | 44 + arm7/Makefile | 127 + arm7/source/emusoundfifo.c | 83 + arm7/source/main.c | 95 + arm9/Makefile | 141 + arm9/data/clickNoQuit.wav | Bin 0 -> 10628 bytes arm9/data/clickQuit.wav | Bin 0 -> 11374 bytes arm9/data/mus_intro.wav | Bin 0 -> 26420 bytes arm9/ds_arm9_hi.mem | 11 + arm9/ds_arm9_hi.specs | 8 + arm9/gfx/bgBottom.png | Bin 0 -> 36593 bytes arm9/gfx/bgFileSel.png | Bin 0 -> 15706 bytes arm9/gfx/bgInstructions.png | Bin 0 -> 4625 bytes arm9/gfx/bgOptions.png | Bin 0 -> 16240 bytes arm9/gfx/bgTop.png | Bin 0 -> 25202 bytes arm9/source/ds_tools.cpp | 1090 +++++ arm9/source/ds_tools.h | 36 + arm9/source/emucore/AY38900.ITCM.cpp | 1058 +++++ arm9/source/emucore/AY38900.h | 263 ++ arm9/source/emucore/AY38900_Registers.cpp | 198 + arm9/source/emucore/AY38900_Registers.h | 37 + arm9/source/emucore/AY38914.ITCM.cpp | 339 ++ arm9/source/emucore/AY38914.h | 101 + arm9/source/emucore/AY38914_Channel.cpp | 1 + arm9/source/emucore/AY38914_Channel.h | 25 + arm9/source/emucore/AY38914_InputOutput.h | 16 + arm9/source/emucore/AY38914_Registers.cpp | 180 + arm9/source/emucore/AY38914_Registers.h | 36 + arm9/source/emucore/AudioMixer.cpp | 161 + arm9/source/emucore/AudioMixer.h | 54 + arm9/source/emucore/AudioOutputLine.cpp | 31 + arm9/source/emucore/AudioOutputLine.h | 29 + arm9/source/emucore/AudioProducer.h | 26 + arm9/source/emucore/BackTabRAM.cpp | 79 + arm9/source/emucore/BackTabRAM.h | 52 + arm9/source/emucore/CP1610.cpp | 4258 ++++++++++++++++++ arm9/source/emucore/CP1610.h | 159 + arm9/source/emucore/CRC32.cpp | 102 + arm9/source/emucore/CRC32.h | 28 + arm9/source/emucore/ECS.cpp | 51 + arm9/source/emucore/ECS.h | 53 + arm9/source/emucore/ECSKeyboard.cpp | 85 + arm9/source/emucore/ECSKeyboard.h | 49 + arm9/source/emucore/Emulator.cpp | 264 ++ arm9/source/emucore/Emulator.h | 110 + arm9/source/emucore/GRAM.cpp | 79 + arm9/source/emucore/GRAM.h | 44 + arm9/source/emucore/GROM.cpp | 15 + arm9/source/emucore/GROM.h | 27 + arm9/source/emucore/HandController.cpp | 107 + arm9/source/emucore/HandController.h | 77 + arm9/source/emucore/InputConsumer.h | 42 + arm9/source/emucore/InputConsumerBus.cpp | 46 + arm9/source/emucore/InputConsumerBus.h | 27 + arm9/source/emucore/InputConsumerObject.cpp | 65 + arm9/source/emucore/InputConsumerObject.h | 53 + arm9/source/emucore/InputProducer.h | 38 + arm9/source/emucore/InputProducerManager.cpp | 60 + arm9/source/emucore/InputProducerManager.h | 43 + arm9/source/emucore/Intellivision.cpp | 336 ++ arm9/source/emucore/Intellivision.h | 52 + arm9/source/emucore/Intellivoice.cpp | 16 + arm9/source/emucore/Intellivoice.h | 40 + arm9/source/emucore/MOB.cpp | 204 + arm9/source/emucore/MOB.h | 81 + arm9/source/emucore/MOBRect.h | 25 + arm9/source/emucore/Memory.h | 26 + arm9/source/emucore/MemoryBus.cpp | 230 + arm9/source/emucore/MemoryBus.h | 46 + arm9/source/emucore/Palette.h | 14 + arm9/source/emucore/Peripheral.cpp | 114 + arm9/source/emucore/Peripheral.h | 236 + arm9/source/emucore/Processor.cpp | 49 + arm9/source/emucore/Processor.h | 61 + arm9/source/emucore/ProcessorBus.cpp | 318 ++ arm9/source/emucore/ProcessorBus.h | 65 + arm9/source/emucore/RAM.ITCM.cpp | 157 + arm9/source/emucore/RAM.h | 72 + arm9/source/emucore/ROM.ITCM.cpp | 174 + arm9/source/emucore/ROM.h | 73 + arm9/source/emucore/ROMBanker.cpp | 24 + arm9/source/emucore/ROMBanker.h | 27 + arm9/source/emucore/Rip.cpp | 843 ++++ arm9/source/emucore/Rip.h | 87 + arm9/source/emucore/SP0256.cpp | 956 ++++ arm9/source/emucore/SP0256.h | 125 + arm9/source/emucore/SP0256_Registers.cpp | 51 + arm9/source/emucore/SP0256_Registers.h | 28 + arm9/source/emucore/SignalLine.h | 30 + arm9/source/emucore/VideoBus.cpp | 93 + arm9/source/emucore/VideoBus.h | 37 + arm9/source/emucore/VideoProducer.h | 27 + arm9/source/emucore/knowncarts.cfg | 696 +++ arm9/source/emucore/ripxbf.c | 144 + arm9/source/emucore/ripxbf.h | 120 + arm9/source/emucore/types.h | 174 + arm9/source/main.cpp | 40 + knowncarts.cfg | 699 +++ logo.bmp | Bin 0 -> 630 bytes 99 files changed, 16793 insertions(+) create mode 100644 Makefile create mode 100644 arm7/Makefile create mode 100644 arm7/source/emusoundfifo.c create mode 100644 arm7/source/main.c create mode 100644 arm9/Makefile create mode 100644 arm9/data/clickNoQuit.wav create mode 100644 arm9/data/clickQuit.wav create mode 100644 arm9/data/mus_intro.wav create mode 100644 arm9/ds_arm9_hi.mem create mode 100644 arm9/ds_arm9_hi.specs create mode 100644 arm9/gfx/bgBottom.png create mode 100644 arm9/gfx/bgFileSel.png create mode 100644 arm9/gfx/bgInstructions.png create mode 100644 arm9/gfx/bgOptions.png create mode 100644 arm9/gfx/bgTop.png create mode 100644 arm9/source/ds_tools.cpp create mode 100644 arm9/source/ds_tools.h create mode 100644 arm9/source/emucore/AY38900.ITCM.cpp create mode 100644 arm9/source/emucore/AY38900.h create mode 100644 arm9/source/emucore/AY38900_Registers.cpp create mode 100644 arm9/source/emucore/AY38900_Registers.h create mode 100644 arm9/source/emucore/AY38914.ITCM.cpp create mode 100644 arm9/source/emucore/AY38914.h create mode 100644 arm9/source/emucore/AY38914_Channel.cpp create mode 100644 arm9/source/emucore/AY38914_Channel.h create mode 100644 arm9/source/emucore/AY38914_InputOutput.h create mode 100644 arm9/source/emucore/AY38914_Registers.cpp create mode 100644 arm9/source/emucore/AY38914_Registers.h create mode 100644 arm9/source/emucore/AudioMixer.cpp create mode 100644 arm9/source/emucore/AudioMixer.h create mode 100644 arm9/source/emucore/AudioOutputLine.cpp create mode 100644 arm9/source/emucore/AudioOutputLine.h create mode 100644 arm9/source/emucore/AudioProducer.h create mode 100644 arm9/source/emucore/BackTabRAM.cpp create mode 100644 arm9/source/emucore/BackTabRAM.h create mode 100644 arm9/source/emucore/CP1610.cpp create mode 100644 arm9/source/emucore/CP1610.h create mode 100644 arm9/source/emucore/CRC32.cpp create mode 100644 arm9/source/emucore/CRC32.h create mode 100644 arm9/source/emucore/ECS.cpp create mode 100644 arm9/source/emucore/ECS.h create mode 100644 arm9/source/emucore/ECSKeyboard.cpp create mode 100644 arm9/source/emucore/ECSKeyboard.h create mode 100644 arm9/source/emucore/Emulator.cpp create mode 100644 arm9/source/emucore/Emulator.h create mode 100644 arm9/source/emucore/GRAM.cpp create mode 100644 arm9/source/emucore/GRAM.h create mode 100644 arm9/source/emucore/GROM.cpp create mode 100644 arm9/source/emucore/GROM.h create mode 100644 arm9/source/emucore/HandController.cpp create mode 100644 arm9/source/emucore/HandController.h create mode 100644 arm9/source/emucore/InputConsumer.h create mode 100644 arm9/source/emucore/InputConsumerBus.cpp create mode 100644 arm9/source/emucore/InputConsumerBus.h create mode 100644 arm9/source/emucore/InputConsumerObject.cpp create mode 100644 arm9/source/emucore/InputConsumerObject.h create mode 100644 arm9/source/emucore/InputProducer.h create mode 100644 arm9/source/emucore/InputProducerManager.cpp create mode 100644 arm9/source/emucore/InputProducerManager.h create mode 100644 arm9/source/emucore/Intellivision.cpp create mode 100644 arm9/source/emucore/Intellivision.h create mode 100644 arm9/source/emucore/Intellivoice.cpp create mode 100644 arm9/source/emucore/Intellivoice.h create mode 100644 arm9/source/emucore/MOB.cpp create mode 100644 arm9/source/emucore/MOB.h create mode 100644 arm9/source/emucore/MOBRect.h create mode 100644 arm9/source/emucore/Memory.h create mode 100644 arm9/source/emucore/MemoryBus.cpp create mode 100644 arm9/source/emucore/MemoryBus.h create mode 100644 arm9/source/emucore/Palette.h create mode 100644 arm9/source/emucore/Peripheral.cpp create mode 100644 arm9/source/emucore/Peripheral.h create mode 100644 arm9/source/emucore/Processor.cpp create mode 100644 arm9/source/emucore/Processor.h create mode 100644 arm9/source/emucore/ProcessorBus.cpp create mode 100644 arm9/source/emucore/ProcessorBus.h create mode 100644 arm9/source/emucore/RAM.ITCM.cpp create mode 100644 arm9/source/emucore/RAM.h create mode 100644 arm9/source/emucore/ROM.ITCM.cpp create mode 100644 arm9/source/emucore/ROM.h create mode 100644 arm9/source/emucore/ROMBanker.cpp create mode 100644 arm9/source/emucore/ROMBanker.h create mode 100644 arm9/source/emucore/Rip.cpp create mode 100644 arm9/source/emucore/Rip.h create mode 100644 arm9/source/emucore/SP0256.cpp create mode 100644 arm9/source/emucore/SP0256.h create mode 100644 arm9/source/emucore/SP0256_Registers.cpp create mode 100644 arm9/source/emucore/SP0256_Registers.h create mode 100644 arm9/source/emucore/SignalLine.h create mode 100644 arm9/source/emucore/VideoBus.cpp create mode 100644 arm9/source/emucore/VideoBus.h create mode 100644 arm9/source/emucore/VideoProducer.h create mode 100644 arm9/source/emucore/knowncarts.cfg create mode 100644 arm9/source/emucore/ripxbf.c create mode 100644 arm9/source/emucore/ripxbf.h create mode 100644 arm9/source/emucore/types.h create mode 100644 arm9/source/main.cpp create mode 100644 knowncarts.cfg create mode 100644 logo.bmp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..62ae9b4 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +#export DEVKITPRO=/opt/devkitpro +#export DEVKITARM=/opt/devkitpro/devkitARM +# +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +.SECONDARY: + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +export TARGET := NINTV-DS +export TOPDIR := $(CURDIR) +export VERSION := 0.5 + +ICON := -b $(CURDIR)/logo.bmp "NINTV-DS $(VERSION);wavemotion-dave;https://github.com/wavemotion-dave/NINTV-DS" + +.PHONY: arm7/$(TARGET).elf arm9/$(TARGET).elf + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all: $(TARGET).nds + +#--------------------------------------------------------------------------------- +$(TARGET).nds : arm7/$(TARGET).elf arm9/$(TARGET).elf + ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf $(ICON) + +#--------------------------------------------------------------------------------- +arm7/$(TARGET).elf: + $(MAKE) -C arm7 + +#--------------------------------------------------------------------------------- +arm9/$(TARGET).elf: + $(MAKE) -C arm9 + +#--------------------------------------------------------------------------------- +clean: + $(MAKE) -C arm9 clean + $(MAKE) -C arm7 clean + rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9 diff --git a/arm7/Makefile b/arm7/Makefile new file mode 100644 index 0000000..3a120ed --- /dev/null +++ b/arm7/Makefile @@ -0,0 +1,127 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# 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 +# DATA is a list of directories containing binary files +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD := build +SOURCES := source +INCLUDES := include build +DATA := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb-interwork + +CFLAGS := -g -Wall -O2\ + -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\ + -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM7 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -fno-rtti + + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,-Map,$(notdir $*).map + +#LIBS := -ldswifi7 -lmm7 -lnds7 +LIBS := -ldswifi7 -lnds7 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + + +#--------------------------------------------------------------------------------- +# 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 ARM7ELF := $(CURDIR)/$(TARGET).elf +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) *.elf + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(ARM7ELF) : $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/arm7/source/emusoundfifo.c b/arm7/source/emusoundfifo.c new file mode 100644 index 0000000..0bf7780 --- /dev/null +++ b/arm7/source/emusoundfifo.c @@ -0,0 +1,83 @@ +#include +#include +#include + +typedef enum { + EMUARM7_INIT_SND = 0x123C, + EMUARM7_STOP_SND = 0x123D, + EMUARM7_PLAY_SND = 0x123E, +} FifoMesType; + +//--------------------------------------------------------------------------------- +void soundEmuDataHandler(int bytes, void *user_data) +{ + int channel = -1; + + FifoMessage msg; + + fifoGetDatamsg(FIFO_USER_01, bytes, (u8*)&msg); + + switch (msg.type) { + case EMUARM7_PLAY_SND: + channel = (msg.SoundPlay.format & 0xF0)>>4; + SCHANNEL_SOURCE(channel) = (u32)msg.SoundPlay.data; + SCHANNEL_REPEAT_POINT(channel) = msg.SoundPlay.loopPoint; + SCHANNEL_LENGTH(channel) = msg.SoundPlay.dataSize; + SCHANNEL_TIMER(channel) = SOUND_FREQ(msg.SoundPlay.freq); + SCHANNEL_CR(channel) = SCHANNEL_ENABLE | SOUND_VOL(msg.SoundPlay.volume) | SOUND_PAN(msg.SoundPlay.pan) | ((msg.SoundPlay.format & 0xF) << 29) | (msg.SoundPlay.loop ? SOUND_REPEAT : SOUND_ONE_SHOT); + break; + + case EMUARM7_INIT_SND: + break; + + case EMUARM7_STOP_SND: + break; + } +} + +//--------------------------------------------------------------------------------- +void soundEmuCommandHandler(u32 command, void* userdata) { + int cmd = (command ) & 0x00F00000; + int data = command & 0xFFFF; + int channel = (command >> 16) & 0xF; + + switch(cmd) + { + + case SOUND_SET_VOLUME: + SCHANNEL_CR(channel) &= ~0xFF; + SCHANNEL_CR(channel) |= data; + break; + + case SOUND_SET_PAN: + SCHANNEL_CR(channel) &= ~SOUND_PAN(0xFF); + SCHANNEL_CR(channel) |= SOUND_PAN(data); + break; + + case SOUND_SET_FREQ: + SCHANNEL_TIMER(channel) = SOUND_FREQ(data); + break; + + case SOUND_SET_WAVEDUTY: + SCHANNEL_CR(channel) &= ~(7 << 24); + SCHANNEL_CR(channel) |= (data) << 24; + break; + + case SOUND_KILL: + case SOUND_PAUSE: + SCHANNEL_CR(channel) &= ~SCHANNEL_ENABLE; + break; + + case SOUND_RESUME: + SCHANNEL_CR(channel) |= SCHANNEL_ENABLE; + break; + + default: break; + } +} + +//--------------------------------------------------------------------------------- +void installSoundEmuFIFO(void) { + fifoSetDatamsgHandler(FIFO_USER_01, soundEmuDataHandler, 0); + fifoSetValue32Handler(FIFO_USER_01, soundEmuCommandHandler, 0); +} diff --git a/arm7/source/main.c b/arm7/source/main.c new file mode 100644 index 0000000..032133f --- /dev/null +++ b/arm7/source/main.c @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------- + + default ARM7 core + + Copyright (C) 2005 - 2010 + Michael Noland (joat) + Jason Rogers (dovoto) + Dave Murphy (WinterMute) + + 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. + +---------------------------------------------------------------------------------*/ +#include +#include +#include + +extern void installSoundEmuFIFO(void); + +//--------------------------------------------------------------------------------- +void VblankHandler(void) { +//--------------------------------------------------------------------------------- + Wifi_Update(); +} + + +//--------------------------------------------------------------------------------- +void VcountHandler() { +//--------------------------------------------------------------------------------- + inputGetAndSend(); +} + +volatile bool exitflag = false; + +//--------------------------------------------------------------------------------- +void powerButtonCB() { +//--------------------------------------------------------------------------------- + exitflag = true; +} + +//--------------------------------------------------------------------------------- +int main() { +//--------------------------------------------------------------------------------- + readUserSettings(); + + irqInit(); + // Start the RTC tracking IRQ + initClockIRQ(); + touchInit(); + fifoInit(); + + //mmInstall(FIFO_MAXMOD); + + SetYtrigger(80); + + installWifiFIFO(); + installSoundFIFO(); + + installSystemFIFO(); + + installSoundEmuFIFO(); + + irqSet(IRQ_VCOUNT, VcountHandler); + irqSet(IRQ_VBLANK, VblankHandler); + + irqEnable( IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK); + + setPowerButtonCB(powerButtonCB); + + // Keep the ARM7 mostly idle + while (!exitflag) { + if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) { + exitflag = true; + } + + swiWaitForVBlank(); + } + return 0; +} diff --git a/arm9/Makefile b/arm9/Makefile new file mode 100644 index 0000000..6f64548 --- /dev/null +++ b/arm9/Makefile @@ -0,0 +1,141 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# 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 +# DATA is a list of directories containing binary files +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD := build +SOURCES := source/emucore source/common source +INCLUDES := source/emucore source/common include +DATA := data +GRAPHICS := gfx + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +#ARCH := -mthumb -mthumb-interwork +ARCH := + +#CFLAGS := -g -Wall -O2 -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math $(ARCH) +CFLAGS := -Wno-address-of-packed-member -Wno-multichar -O3 -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math $(ARCH) + +CFLAGS += $(INCLUDE) -DARM9 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -Wno-conversion-null + +#ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s +ASFLAGS := $(ARCH) -march=armv5te -mtune=arm946e-s + +LDFLAGS = -specs=ds_arm9.specs $(ARCH) -Wl,-Map,$(notdir $*.map) +//LDFLAGS = -specs=../ds_arm9_hi.specs $(ARCH) -Wl,-Map,$(notdir $*.map) + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lfat -lnds9 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + +#--------------------------------------------------------------------------------- +# 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 ARM9ELF := $(CURDIR)/$(TARGET).elf +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(PNGFILES:.png=.o) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) *.elf *.nds* *.bin + + +#--------------------------------------------------------------------------------- +else + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(ARM9ELF) : $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin + @echo $(notdir $<) + @$(bin2o) + +%.s %.h : %.png +# grit $< -fts -W3 -gT! -gzl -gB16 -gb -o$* + grit $^ -o $@ -gt -mrt -mR8 -mLs -gzl -mzl + +#%.gif.o : %.gif +# @echo $(notdir $<) +# @$(bin2o) + +%.wav.o : %.wav + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPSDIR)/*.d + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/arm9/data/clickNoQuit.wav b/arm9/data/clickNoQuit.wav new file mode 100644 index 0000000000000000000000000000000000000000..16a9253aeed54c3f24f476e6cc1be763ee6cb5ad GIT binary patch literal 10628 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 MA)SUS5bXbd0I~PMB>(^b literal 0 HcmV?d00001 diff --git a/arm9/data/clickQuit.wav b/arm9/data/clickQuit.wav new file mode 100644 index 0000000000000000000000000000000000000000..1c642e16f79ce8a018a707bc3099e042c2a1a079 GIT binary patch literal 11374 zcma)?*>YRgm4=nOzRV+Z<+@*@yQ))_+gNeplxPhm34oX<0w6$QAO;a20D>5Zk&|Rg zmL>!K+!HQhzA3KkN+C?K1cZDAAbM)_5ZnY<R8%V!OY+Y=b?)$Dk^hzqZxxd}`^3H^ zpWqYcf&RvO^a7VZFCXNuy>F2h<&_I>RsndkeYd^8@cEK?gZpcKMqhM4YXm!Yq8K0X ze#(8a^s4iw3$M}hZjgNJc_5Km)$@wY9a?4?eg> zyLk)uAAq>F?h6Prly2O6_Vml=f0x^rFJ3%<{^G?yUhwB1eE9s!FQ32o;?Zn7k&H!x z!B8j|2yDPV7@&&fy)ylP2K*bLWO@GCXHPzR^6bgur=LG%Za#nZ1%G%zuJF>|?j7Vd z;}KB-BD|+iq5uVNl}P>>{K}7*Z$z{E_Z~cW@X4cx4?f}X(Zh$2K7I7*-(Wa=`Uv*m zPB#`5SSp~4*-nU1Hr`3^xns?2Or?BAFADzyN{HPs{cj^6Ij?uvbgO@eFFQuN$ zx}{tqN+LDdB#G}N303Y9PR27)J^<4u-1QwPvN<}LA67E)SOm?E9H~mF&(-Bh(o$OY z2N6#&k$00O4W^rPxV{+e6*l7try@<(S@cUx9VBS_aJoL6%ojG}a(^L5r}JTbCmoLj zHv)8CytiHHt#~s@SW4agG7n!os5MrKaEalcaiDw z9SLJ4v6hm%gYg21=S`S`bX`9?JDCk?*;LeY(UR}pBoRrbFA&b`^=BGKRf!5UREZ~a z9*^6l3`*XVyAOke&VvB+l#|hk92Ghvw5F7jn<7x@^||G>$%A| z?`)2Ou~PSFj`}piLu1LD)9*8Rx;ScXZ^kh}IOVv|c^h7tGL22mBkh&`WF`YyMu<6> zsVr(SRNibIq#6>Ip;I8-*0Mb+(pdjIar%=Dmq$A7RvO?qTJSWdi{)=2dxic04_( z=2KiboKn&mqMhVAugwYHx0*waWR{YND%0hJQ*`z#*|cZMqm-=5i~Ww*{h?H~H*j^b zomc`)jbkUXzLY!$vec)u9$k`mFny-vPf(y%C6_sxVM=T6VkM4X!yrtZASRqkLUk+! z6O{vwWN*9k4=7Yio}+Fn!PeAnUz*A83~@#s<2kbBshPqMHAqp)6|RL*2c%BqrRuyO zlOqH>FzjqFmbm{C=T#z(Xm~YjmRdzHZPx9=VY0rQb}$2xT$8&x?&BcO-qdpqC`R(M zQ%uLB!2oqK=S;hfB^k-4U)AF2QXOX91mQYH%12c65rr`o5QYD~ub=Wymxo&-H^z66Y!Pg73jZkt>Jdw3--EPe8e2 zXqB?u%R7_K5T!UpsvLA8`=pI7*~^1TsbC{xi}AjcJa);lTe1_G0d<>*(PVXEdZOgf z?&2_H>c*Riv55y9Be}#ASZO0&hc)w;La|xeY;ZF#P3cX&!{jc6JDYeE`&p66>8a6N z)@g*2xp#PEm~IQ9mJAekXw8EXb~e~7$x_VvlmfjV(=svUYeTfAYbw@pUP^w@*uiH0 z(oyG5Pjj|~c!o2#hq4lDM2%D@pk$-n8{6C@yBlw(;k`&(@o5e%)YA|uC$?UT}anga!XOQufEt8uj zEhbjIg$Ba8=0G>|dQNO|?@2qD6YlD8F+_DP?dmQfdFdv+)jmRwSlXVI!;8dN>g{4z zXQn5JChv59DV~~mTw^G9anix1!)Z)BwgoTur;9;dS52eS3_I<2=}OmiOwVAl(!<8v zS%>Vp;T3wyb4?j_=pn6Svs6rqEMW~)w5s*kN=y;i)s1I!J=fyI^VAYF zA_iF9o5c)H;^YFLolUkoy*gDUT64G0Ya!So=}oClIJJ%Tiae2lOu4b}r9R(sU8Wtb zF;sg;vcgHn!+k1skdpV4=>c{jT#22tO=%-f-aYX|zvJ2xPU5(=vBvy3Q2M?&+S}g5 zbqmRnh;+xiH$x_#%zN*MTN^olRgo@HWMYNe+lKFGcbY;WlsT4Y%4%d=ljqrKj$~}+ ztxDdbsf4D7(^;>Y=cvPaq}3_;ahY7=M6cw?7r;r!aDt^+b|4I?NOdC$ICdes*oE-A z?{dL|>dm^DpLD&2jki5B(9xulC8|8|S_s;mI|ivulXTCl^JIu4B$AU|9c7wwvK2}k zH#t}~<(iG8-PDq&kKC10t5dEA)CXhu?wSO^8{OmCpk9zmb#xv#X(VUT%WT4UlG@}_ z96)^zCi6M2D&j(z8u7ND7~jRXi<2W%d1g}KdVBH0b^@$u|vpE{%P-3mpaO3#y_GzSTt$x`cZFqutfj+r=E>S8e) zcPd-y1h(GT*s-G6_tN?9LT8aapFa?cZ?_JIEqSTu0Q{UK_z!rdVrsdV}F;baW|oI-QJqt$Hz^NhRRD zKpm`w8Y+>2jZi$dQ{4lT(9sc1$)Z+*Fc}XInzd3Pn@(Dx4pRzMYKk~oU?Uh#Wbq${ z``tr@0!y$FtlY_X)ZZ6o0!**cCI1D;<)6%*N~6{3^#?;F%9x84`e@LpSGKp(a9*Jr z09&l!NCjYBAyS$3X6N9r52gppKp45B(V*L?AW3xo=b*&V@ZWV&>a4^0-k~T}uvhTW zXf*7#3{!nE^fM_4@+^)ioQ4(XfBeYV7XhGjLvoBC65IQUHX^ zLnsuErLu)mwb5)-D4}XD8IK0N4s;gtxy^KfP`Xc3DqFxI0!d~D|5IFV?spD)P)d7? zJ2>pN8r4z(0Zk zEF6wN=N6a^2qlyOc-!Fn?P|FQp_{1`+*heat*5a7ghk_ImaDg$txmUlKq|q9OXx%B zw9GW2E>+_yD0_&2xw*An+O6M)P$)G}+6o1i=0SGbU^1Zj9H5{k)&Hfq zDjUdH0y^_MB?@is(|ysZRD0}p_M5lKgir&eRKsOuIi&@#%dFNL%~or_-GPB?OsHKl zp%czSXqHMD0BY61n$i+5{{VDmw+h8_wbp?5ElRa!!QGB9k)+}_gl0Dl)T7l>NsPr4 zsdNU+QUwv+6R2vnV7YI%ptDvf7q<)fEeN$xmCDL;OEwmdC&@%WA+)|{p!;nJfs5U5 zHE%b-r1KE!LQMcn2$UU9z;|Wt*6IzbbRX7s!TZYGrSl5?C2As(1T&Z4rt=Dg|A^|c zwcvY$Ur&*5r%Pms!FlJLbO^&ak%$^clbaLM1yP-g+vgl{(iT1TQ_iWM{u`dz2p zGQaG!Q;?7nilS1ggC&yT1k*UbTXnxM#lTuK-=CuMmq10UtCp@qrv?-eHBkK;mZSDI zWb!*%$}~z5)McrLidbYEo#K5}m1+PO1Y=-UQA~7Rn2!7Ezmdw|!VD7u6(+tfR^#`@ zXqHEnlu4a*-XoMs4b=peM95@33#=xlN!_J$7uA{8o9RV*Y~H0UsS zL5b8UO8H^Mc#eG0gH-=1R|w`{fpi=OnF@8VLJ_)oWSB-MKVZ}fKmx36eJ*dXOjoS~ z(+A=R5BhdF>gO*znm0Kgk_SIWT!vmscK_%yQlDK=Xy4*~$UgE)wXgBse&W~Mdq4LJ bkADryea|m|mVV`a;@3S|`tT)BE}{Gfa!nlN literal 0 HcmV?d00001 diff --git a/arm9/data/mus_intro.wav b/arm9/data/mus_intro.wav new file mode 100644 index 0000000000000000000000000000000000000000..99e77caeb34c48f5eb81c81509a6eba4aef8b6e5 GIT binary patch literal 26420 zcmW)|UvS%2nlA{DavGqNy8uWzq@A8307|Bn%oYKVl5D4E#6Pl~PFJlZ%55iib}P2h zll07Pm2G7<>D%|=kaFe%q?`ur^b`P6Hl_4z5dbO6cF!#m03{{q2Z^L?ch5dpq8+Da z_QA0;8|ThG*jBo-CnM`Yra%xp#QDD8zfTB)Admlt`!D|zd;IUSc>sQ}9(e_TFTiha zh`ai*-_5>u2)y#z`ftH+k7BQ^-T3Ur$wO=Ib@v@G_8Nv=IN$r*&m!PQYi{h08~cm= zSbpqwWbJ7Ee17b;`gw5M{Yu}_LjZuspWSd{k0a|5@XGDT`uRg^H-39G-_!RQd?on* z2>bzj0Z;M$nT`UmeiDO6{s_6_eq{|lac*o4`yKdgrd|Gxjiu=ifT zz?m$32@eLY^sQqUwhC^;-(2{Q-ve;wgXew_#`wq?8WX_>=ivhx01y#*G6w+sJ_FXf ztIpBIm*QA>d&l`9OCnj+U%qc~(b;f6od!F`!5G2CFUsrNH&K2Or#vIdPHB%9_^Gwt zjjgswFXjUKgu=5Akr06r2;m2-_$gE*Ne+B&4GqR|qO$i!=VpdO7fLF@J-a%TMls9i zbIZ4wNLFpA>B$GhSCc%sojb~4Rw|km@01eWFU|eQB=VIf?9!@10^8VX$eCwXaXuvK z*3Z3)PMmgUWUy8yesp1o#BM@ZfFr1-8`PB*F=0xwtF-&0@0G3K)^lSwbDfwKu}!~dp$l<-9C zV_i!HftLT<{-Bea$SPOc7`IR=kyxWv+%xTYgt1;wYlhPwhzQ$X(DUykF`b%W(V7)G zeM2HS+PJ!e6Zq18bhdD-fl!4DeyV)CYw!0WJN^N0#w?xXvw$6@Mg4yY_oE6u@SIrD zL}t;BMAvqfN>V{<6T#o9OLi%IL&09iDlG#HhneHTE)3HNb{{zq<22PYT*DwZ{Kxtu z%M1W1J8pluVQI^=eeXvuwKlX~HX7#NqYzP+*i$5XI;)oFB?5rR3EH}_m8;vB=&S_Y0~V*`n7%PNXN@Y)eJtHW!KVDHXwnGJnT0&Dmwa<<)Q&6l?jp}+8D zLoF(mA5A4txr{%Lc_wF)ZlGFN_(tS}k6qr{(xnEL@_|;TVFdp9hp2$)&G}v_TOJv3 zU2my$vd7P#ATgsC!+cv~1G8Ft1H%;K!fvF|-qPy&);p2&qFPeW#meY=tf-Wq2YPT{ z)VrZ>;e@E4>v7}eRRt^`~lrJ?;nB0maT*dhkM*ezP z+UG4_FtBJyJ#h9p5zC7c83hc+t>T3o3EH0bFDzof>85nb&YJ)W%}*oAtE zr33vJyL9q)y;aN&C>sH9*7&*-bQc_Y1g+SY!LY0z^-`G~Es$`}5yMEMQJag7nm^{S zb>|+&|GJ(DWz?r7Y=9K^`w&dKxqqxjEr&4uaaB(x%O;*cTPp!3yWGgWyWp5T?4XG6 z=d#Pl=x=v^HARa<>)k4uD(?xpi{Gh1G)T!U`;1RE1a!OjWXK;88Jw1A%sxjNXyjbeBh zv-Wr^OG)r3e_%jz#%BL$YkVC$dtS9NVCTiSPgFtt`t#hBhL5@=|9grOaJdkX{p51p zCfbpi&#Iw&{WphLJ%MZ65F~YqIUq)=a zGsUtTC|7?H<>CmYZnVL$kyr0tmgvmFR@dngJex79HRI>JkL1yo^G1{M${!D8^zb6aaPqi zNq2xUyB+*kKccE%4kdU$qKe%}L}GchXj#T>xNNfOiBwll*nnvJ^@h{P{l746=4SeC45;ER`1lcc)S3rKdVtmQRX zKEhnDT2}JX(U5E2vmH2@^dg(u9`I_Em+y8xSTF{xuIb^-pRx;%(})F6Qw7u0^{0|i zq$UwTK2B@RYW^Vkg50T^7*J;lTTa!8xyDe{A#e_s8qf&DSznR|MU9AsEAv{j*1)k7 z%bjY~O3I^)ohB-dqCbY)fC({aFRL!I?}kD&cgM~%J`rgZAJGWstyOI7`!|*KNJ23;jodJqzJz(yo18n% zc}w|*bikKP+ogKIH?RUWq_9B88r7X5HbN#pF(q|{r?}nOs?r^z$*{lOr(f{C-~d@Y z7A5!%_1+vMB&M!wWkXB3X)oAO9${IJ#NVPn&b=JHB}rnK5AhFsH+t#Nz|=ynV)#eT z@#l+~p3eSR5wTWMf7nYRt61w0{OERft1mI4d4yVi(QR#TQ*KeqY;y&G8sA~lgN~gbC?}cxtvNouHH)f*7sJA&h=O|C5!n}){n~j$M~HQ6=sg+`Yeruv$)hS3 z(knB+Rs%5`19w_QWgntEG)=`hdgEC>8bs*NoKlh*cP-}-20hMx_Mf6;MXGF@sc0iF zsanZ{fBCG)q#IiOs}h}7bPwuB7-sQrQ^QHZ@`x`@@1tZKk#KF}*=3yZBVX>ADmIQ* ziWs3rg~xx!vo!ZKui?=VpyY7GDwe8!lQE=XwJu|POjJeDgAm}mIGH7uSH*VtoLeHr zmIqy4A%o}{pW4Rm;-_X+xY%z97dvK(O8N>-8PMu!AT!YwiJBbkox1#^!AdD62VWOIV#DV{zwxMJ;GemEom{1tdplFH48@;T#y;L!GD-CGM^+0F^+Ty$imsafM-L)0Yu3vgcOu1cbx3o&i2<#}T`N^cmjMkMIuM_<}#ga6sArdEpw zPbC@gmg*-dCUn>AmBKNex?9s!G+Ib}v@y>Eda9>lNHTJEU$#*()c1X;*mg?$S(0-7 zX%0hhnxWV9Ij?fu4aCBx7@3TEt2b3YH_4-)YaWCfq%2#L{HcU;?6ayA)iLis6;*P{ zbIvC_T5WVUMbfM05H-#GrfTtOIPse~PI>gIOVwkF5&S39e}de|>lcK58LZr?`r%!4 zV{?fVk>Ewm!^)>ZZq4r_ec8loKjR;pMqU_Z>W{i@HAx}Ax2uecVs2D@vGTF_tfEA- zu9LIH;j+B5r11k6x6F18VO_z0ujOboaj8{|di7I+T+9rEi5}HH0c5FOWJeTL`eRKC zW!2o)iV`j7l=_mu)PFf^wMJavXpa;;400Ii4;3n&^1PWr>sQ-1+XQ$}R7GVHV5(-G zU}*O&Am3xI_tI>1xS;7ymr1zRI#r8d9v4+z=iOkdTO?d(`xk0#DkH)&^d2RHYnKs* zB@u!sIL?Tx{%0;dI$n^Jy3L))DmnEgPyIdz42&x5S?`w8 z6a`vW>7;WJfS9k1TMgPU$!^*GvC)^T^_@9_KJ;E~Z6H~Qx>bJQ}!=HigLtM4|X=-_; zjq`3C$)4bUU-SzId+E%q-ONN%AJmuR6j{(zrK^r5scWl_1dxK`8kgn9vHr!reY86I z@xejT!i_#JHebtGpBL%lpi#tvtZQ9)AEL_$^9M~7IpOh@A+Mf%SUl5%VQi+ZVuMjc zvzkR@QwpQ{Ek_Mz>qGB}u*~{ngfU8gKtH!Ba-UZH0|*W7%mD>|*PVr>Qk=MC&A}!k zi}}W?s+|N`geJw^&&xn zR2FrkD*0Z5Wyq>HnH>_??=VHe#J1kpze}#yi}6d9=P@JyZuAdJq9&dDfuaI)j78~z zj(FA$0;FzDek1|?=Kf`)B$lb4UD~K=G&tK2w}6VB_5#h&S`w^VnAQ9j6~|Xi3}6SgbN!zArkzjOCVMu(BGRBufO?GqjcE7 z=zp!M6oSgPouVXzOhJuH=T3j1h|w5}7DT*y&Q&hj$m%HjU`bQc*+MyIX3~sz@xxRl zcvFZ5iu8n*)6;)`j2Z8G|CRicUG*@}iZ#?P~A z$Lj2-u5H>x96jw-n>5Lv+YMBbN?v;AprnhOy|W}CB*iK>J8>7>u`P~^FXZy~4P$CIugT6M6*E)2A*quJ$_waR1)h^qrjWz;J=uITx1N~#!qi&-%8RU0+| z%rfI_K~u`gSqj-`H#afHRoOsNnRP?XoM3xUwW$RnX|{0R3c`1~qz8kEgiqZ>Y5puL z8szLZmn8(@XYX7_u1fLqB97?T%zu+aRh^j4VCafUM0!NNCDxzi5P6tQ-6)}7<3Yxy zDedl_EM9@z@fW||yAKs3)7jpiihknLl4_(1uV0^+Ea!C>?J}fx@fSTs3fUQQNBUc! zUK|^shc^N!B3kdE#3kKwFg{e+Xd0gfNKCPu>KiOCIGXiec8B+o`u=Mgg7R7Fi_N^~ z-v_{ksfFJrkhQ8`*ea@bXtfo07-+BD(8kU z(_C%$R=Ra%Ggr6N^kHTV*5=>|fhm~LHZk=PesKkXQq1f|vt4|{8=!7USRssbj@Qiq zDG}&;Al2i5@sJBO%o7%P+t(6u+cuRc741 zJm+gFT$W&4*sY`Pg3-}HStPtyox8@tL7u6ng;yDR#Rz}%pWvD%Sn6ZP2+sTPXg(FW3Ug;L+TGpiE73s8aaf#@vqk<4MQ;#(%K*HAWkBxQ)sf1n2 z-w~7xDLdLG{g#(RjBOp8hT=*3iK$_T5@)Yh8@Xw)e`>bs{~5h(iulq5>nb!lcIjk@ z(yEBO^&*RI2CxGcmq?(U`Fn|=r(6Y9Y-}b%UeUz9R$r$8($-X~_tK^ne(NJibZz_3 zv7{m|VnCOnZcWi)1TBV^sIK~N-UTisV?^1||Gdfav zRNQEa!jyZhs8wfB87E28dD9EbX7F8#fU7~4rL&>1=T@)USi0jWR?5092REPus`_^ql2Q- z>Ub8MY7zzYjr9#?hVNOh+WI6rTVKjWGWgLE8-I7!&nn&rs(8myQ0_t1&^u%z=~m{M z<+D8`kez_?%jZ-BfN&Pi9ML?>MffphZV!D>%an!Vi-x$~-S4K|P4ZFuuXc%|E(TCx zw+bE)MTC`c@{^acsvY5H!GB-534utYAJf|H1F-?b*pEiWW4XLy7iW6pB}D=`>(67N zhTXO9^IK2Me%gzfn^!#qzh>*>EpsB&2YrTbXG1uLXcz3MTbaG z4@$*cg1>0)G#g1YKD&06gf#hD#}+5~9?^l-XK~ejlW|Uw{De+XMCa+^y^y9u1-STVDYLpe>CCC)p#=2o+EAQpJ9GEI9zx^G*q(@kFXec3NWPO)o0x{FD%)%vJjr z5DPhu{bD@C0JQEHCB)tR#dAParTC2ZBGe7Uzq!lr*R<`z3S-UDPM+A>Z-VZL^SpwKv`9>v;74})Wgj6Rl6;n zoo3ONVVpcoiK^DkBNR=O2!FP}R%wk!-8CIhK1Nhu(A=1I1{ zyi(1*d74Fxwt>?e)4JIUNtZ$*Kb!W#RqFOe+)lTJonqOVjvqb8Q%=WzDJcZ)cK1cj z-68b@=bi&V$0P50MP1Sk@#1%q8lLP2Mn_Uk!pDqlI)czlES;w1R$F63)SdQam6SnU zB!&+Q|HIHkcAUyA*i95~^vdexZ-w0D|Yv`h<~1i$PNrQyu8l7xuq^sHjEOoBWr&=ptrTj=VbDSlBis+w_j zR>X#R`P*tvG@{9mH*DKb|1RpoD9HBpDvnPJzirnvOgqL>Tzb0vv?%K6;V3Lhj-gI4 zB#Em#PA{q=C&-MX*9>gNN3%kx+?|uFMBM$2X*o@7f)`lQ)3Sf)l67Ia@MY1wrM~G7 zlLBd*N*RnN56_B?)tWX&kpx||FME0kijLhkfJ^N4%ll1I6_iUs4m zS1Hddz1Gq*k$1|U`BWHL&F1QyMzf`W25qAyDd=i=yQSL7+PN%+9u__+=J3H-=4oEhJD_rOfD)5z<_Yw~Lgk0KAjh}cMcu$dA@=$ePb|eQ zbaO$d(GutR_n5lfH6cv_Djt3}hKf(;M0%K#b5b!81Lf_OZ91g`155-;({~y+IwD$o zg^^^#Ztl%=Tr6m{F7rp|0@S0)Ouf@P;Gt#gX%musRLD%xUIN^g_%ut3MdQprUU6pY2rn{`>!}^r&+V}{(XoX z2J1C57>|c29ZHLfFNc!GW*(QY(@OZ=WWT857L?PXbVjzFt4PDz1J-WVaFR(!8IqVe zujUX7KN|~?QnzaNGBJA;ecCpIkHV8)lJH>l=Uqv%D=FP2z25booeCkS-p+>w@ihoHuAUr=?w|p=3esRWt1%N#TDE^CB`}+Qy(pn>7kBaLue=RXJQ+&BmBZ^Lk)Tt) zKXzaV!r)oZWqVWCN8d`ah_1Jx+TkwFoq0}C5Ha$z5UQx1ZZ8!Ul9>aa&MU&d8HZe0 z6g)K)MLK~X6Fn6h(Umj15JcVV4Q4eYR_|TNYyDqJXF_C@Z1rC2)DXVzk-J?w$R#Hy zQ)(xVA>=T#Y6`9gGE)TaPs85u?h6^Fuz$XEcx8>4JLxomk3H zH9Rs1{O#%xlVt98w0EMENI!r929~ZgN}_@uF6gfgB9^qB|2d@HI)0W9YVEH&dqYrb zc&2%1VOZ947?MKQt=9&bj9h>I)qd5|t=>2HA=^g&oiqcT4jAWPRNN@~qe&8}@8Ktd z{Ig$$qp<7mKb;m-d+Gc0QPRKV{Xz;ey7~P)MK+#6IKhaGz`FzTCS>MQNwT5(&qNtB zAU`PvqHV`1?nz8S$w0|P5{9!kxsNfrZ-kNz^Q}=F4Tbov_8gK(A{!7$AThaa7*cAS z1b)W5z1ry|X^y^`JD5(UNO>EsmDjOXWO5Rw3Y?ZY#KcHFasHbk78OE^PphiPdtD1F z9b_tbNi|ERf0}~Xh<5IEE;8&-EO%>~pM*)9E74(!amnqg!L)0+rdJJr!keipH!U?d z!1A$$VsKF4(|>$aB%s7Fmv3B&CzFdq)P{YOCeRwb($*tWr!i&22p}%a|Nq#%BLgHL z7A$L)n*8?&-p{)SHGrlo}r)f$GUX#(@Mt>+6c~D$hl^M_m&}O2!rGAitZ^cWk?|_dSQw>k{6}G+us{#1(gyN;vb9t)w>51E^J(S zMXj5-t12)Y_i^Avvl&1a0!NgcJt%@k84y6GlbhC_j0VLo z8J9IfVi5H}f9AUqhLK!!{jLWnSaKW%;b;<81GUJ#jj8_SW`#gFl$suFY5V^$7d)tOHS>q~2F^2cs_l(NA&iRmhsA1=??PRX;zhLCKo#xw?`ILngJP z>Bj3RfmaoTTHIN^yFU>PWhyNV=VP?hewoTzw~SX=uV~*la{fdtlcC*2`DhUL%N&7B*VJ}s^ z<;;;$PDI9_UHDxqzzIBUY?{4C_rcDLM+CmM8KB)HsNc8yn8ELXlZfWNv-S(o5GUTX zJxr3)?-qw*3DF+nLP%8mf7^4AJF&795S^8kJ&7pGsyB;!Mq`Wumpdg`cJJ6JA*6m~ z&-wWjEkkLj^!+t+(U5Lfhxoye^r$#A8C4p^-00y1xC?71f0)YvKd@Fic+ksCX_hCF zW?Y?4K0F;&D)wAhh|2fO(*9wVgqmpX=~Xd^@IBY7QfAUc!!VVSo^~x%Y&0JGsL`z2 z++Z?yFdF$oRn2fSJm!q1bNj z3+GW04^JmuvRNXy>FgIBNAgc3c?Oe_WfeO`Zq*NRyAkiEQz}Aw;a=~|i#!q`mYYjD zn3!Z;IO@eb*Z}q?2@MTI(e-W1gy|Py;X?&slJOf)ZHP^X2`?9qGGCam#Ui8F#CS+3 z-@TAiL(z<0ENkaz+3dqSy7O=k4?;)kTX}@>PZZ1_A4$>;%dzKDLT0%G6DvNXUIXI| z$L_;U$I_WXxh3M^r^O%dV|lRAH5D>?alP7VQ_00fAH(WfjYoa-NYn+rHy!1kTo_6v z7_|PZq_RnuSkx^l-uky3NfG5Q+ZTv5?=93Fm8PC9dAK7eS$L@7tl$Cp(<@F4<)7m;LgJsE8O+aUEXs(2RE92uun- zRlV6uGKmzjZSSQRg8O0*<#I$L5Eje#XM~G^+0Kfye_9BM)oO7+OGoLguOR$M5(tzN z6|zyPeBQwhvsqC!ZW;KoWXNAN^;cPx;TQg7dO`?YZuK$@8*_bex2DJ7ZKOMnj*lf{ zyxq))85%t-ZxtnKoRPoGA$&%ZAAHru5;1Saa9Wi>kc%dr4o5L7T$;M}2;pSO<2^!=;*)z-g&I#lda9ZEFzd|~ zOJ^vT(sUyITu)9}YMXuRVgE#S`H`b4=b})<(G3g@vQjg73772emckV85;aBAi^O{n z32t}n78ajj-MXUZMj6r%Iay+tVz1(yfVP^ZqaK(XgdgE`eeZo*zrAgh%Rs5(SpNKN<`b{%@itQn*j&0{)%S>cs zO}gKN_8vb7J&sL`z~a$F5PjUeKoBf{y=sb%KS6rEyE{s&JBRS>luOPj&>jZi;}k;` zp5%jZA^CgLf;241pu~V2ez0R2R4mG5`g$5&oH&|DW+dlc5lOXwDr>#ggU9 zm%6HjV(g`y$glS5Dsh6b%vz&}fWeR}Kqc*4wwWIu2C5b&#evxQg=$4rPGV9azZm3>Fj=kUJkJj@^%nHUl4fe(aQJsNu0ub7_+8s{{LJh5P z0SxlN>owH^litb&;s~Oc6|rGq^}h^3Zc049I)@I^P`x$KXmqwx+_34thI%|s^NDZl zq6YWY`w5zOnESF*B2y%?+%7V)&|nzcG`1rDV`wCd!QUjLEc8eYj| zPGFBT7+z+WOl7AgG7RI=FOwPhHE&?d^Q~4k=R&EhQZdQ55hxTF`EdsQq}vu#6nU{> zt0c$VE+HfE_Oz}lF=}~US0I|!pUsm4>_z2Er_HBb0CIEboHr{PRJj_s)NGP(LF|!f zSnAWSZ1D)4Em++u9Tm=thV7hUpeqtW@}5j%RZM(p?>R!)#kEKC)CiSPTP=8-huosn z>K=u0Ks}?_zwn_Q0}G;KDXArvpHRy{)FHytk~g+9>irl%mw1HsMZ`xhgj;NVRotnO?iL2;Z*eSnUGg? z?4xNPP-&SSbh(K8j)&l3Ft73O5o~*DS3$4X+W_UyYxY+#;2?`y^&s|F!V%rjUL6*w zx>b8r5=l0@C>yMC)t`|-@H@{Liqsnw&uDsDXf>-4X+-I@-bzRJj{iqzqV*308Qtt0 zolLKpIk%E!j==rua8s=JqFrsPT3|P71gD8h+G=(G!rGiabeOEk>%HS`OYyDdu9HOFOTg zbIWZcSXiq#H|=!jJmFGS^&kaukdgMb?GfMP6O@GpF^X(kw!AJkVQ zKSN5v_?_H=T}!o~tMXEs?7-(DcoNpQcN*Lh{6L{yvqdk>s@DuIzfuI_|Hga_MZC71dTXjg5<@?oD zSV;U)x=a=R*JMZ~mhB(z&NR#w$3(fe$&8{qHqE6c2Shti$&o&pQ~uOP0wYmw{V>YA z$)`(s#6Qwg&>C$$fd&&k$&l?UD!!FuzUdo{(p0_K8+6A{uXjugd7HX!6g7o*efkpB zmQNw0!9<5~KbY^03<&FX-}ij!PBo86N0N9=FBsARw)V@As|7vX(HM+YDhE;_y#f2>%%CADtv*sfx!ZH2!|j&6XCftN z+R;DEVT|Y(#gik%W==Z54^&j_PBkzL9mC4O5vpScxalM$SCH_Ii%>8*7Gqj%i69Y$ zVjf=!#2725A3Q?Zn^41{6e;rQWt=~}Zsg$*ZxfC!;I5mvIIs+0e zYU1LPB{M2xez-c;z2Z=^pDzasPpbG~ustUcX&Nzf7_U-ho?9@~6C{*X1?=;j)kkwM zY5C^G%OHk6H9TG}?aGK4HgBF`FIuZe`kGOcGzmQ3GZD*d;B+CkTM({&{yel&L)Y)B zF41|NT4*+y85$i6A@@{02Q!D(F_?P_#xf7KHG)T2_eNk`xf^D)FevPT%{3G+-+pQy zJd)5wKZ1qHTHDS=;;EQtHHYgOJ;J#tnAxTWd&)(9$w9L9TN2`*INeh)mLwvZK|_g2 z_w_4M6ow;AF{8Jz_h%b5<6P?3m8!@jL$ivVueDzdkwU}D7r%9MJ{nqVE6Hi9Qfo8_ z>MYEHYIA3j&>nL1Jtt{|BNctVmp(fw@2xDgBylwQgR&FkCaF)B9Bi85DNVa)zmyPy zy7@4VVol{-8iq$1KXbACtQ!&=Id!!o#;1I42-b`HLqdF|b^~m;5Cw&Y>ZbBoAhXw!MoTR~_l(TLkNTSa^RB&XRU$m<24$P}0A=z$rdl7;g z_ANFHnAky;XSi=*1FCjJ_^tHCvc1_w=x7f?D(biS07XTa`dw&0$mjYYe;?0mw2Lwt zJ)EuEFh0%+Jp^C5vpw(elF{STwYI7%>0JdIrLjL1VQMrz<&)cvfyYxW85NiZwpsC} zqCMAZwT2oOKGwt|WTkD-lgwLe!S2`+_pVzM>H4FhCSkGhz}%hW%GzSml^+Y?YG9cin0`mow+a z1|l61Q{Zfl{?1_^iTfvSs0SGzOwfIkCmWXkuib@hTN&lJ-L5$-@qr}cS99TA8M#=h zUKoq{w+ZG03~4*zQ_x>+l)=CJD2A*^k*wc!XLWzvg(5|e{bxyF?&pUv(no)rJ0`f) zu0BdnFwhH1zRtyO5Ds5lQDW#3x`5-#eV7R`J9}|Y-k5kWNmd%P{6GFc$}hC zc2e|Edd+h870LL4Ff7U-t(GQ_WiB!$vnf^>m$Hpiz zKO#Ib{cyCTvAH+S8{1m2CtD58$YP+UhP&em^uG)%<=J*}rKYW4+YjqN|5_AL=Ma|8FxOKtb?H3Gf>jN7JFp*xA`)iP!DR;vs5Zt8}oRCfEq} zZ51DfF^vm25tU$2N2x7g?2Y!Xc<9hUq3SOYonx8aIt-s!-9*Purh4he9`=74aynx^{^eOqMmvDv2{$m{rM4i#Z8DJI((Q24(da|M3>oHcmr7$B-tI z<(AoM`QOcgoI(n-g*#@?Ab)MkgW-Pg44#kj2@8)(nOKM_t9ErG*@NIxO*;1R(+dbs zB5H5RhJE07Dwv`UFE(m9Uz7ya)rv~^c4e$>kO>x+$`rM4R?|=r1dp!(5TDAd+XlNk zMJ-&_E8=JpDw#?+`0q<1HyZXmhh&iL3js`{4l}d=(%A$w}^HinWRfqYofL`uk_8Oe1nywub!af0Uy&Z@$b!Z|2jmA@*& zUc!srqUDGK-b==o+#g-*RwXLQdYNl0j^lStusjQPoVn2{me!k459VPCB6yZhFFPwG z7nNdxeYu#6kp9pJnt9gDamgSen%fPVo1qFhP7H=xC?ZZ!UuON53Ysc71*__!MZZCGMye|l2R?U)MV z>h~64H~3lsadOoEQ|{WAZL5+>lVMk`*-^tG+9Pt;N?TodIJBsn8M(aC__N_)29onX zwe7%E4+SH3+EI#|r0EJw{lV$dQ~WG(a>cCXfLHoy2x?+2l}NK*SO@Oq{LFx>h0&-N39F`;me)!(m0Ej}8AxjPodH85_896}**nU2nsj z0|&Fiy0!vCXAwI1gDRXJ1i2SpK=kJ`^4-b?mP)dcr}4}zi6cyLdsMAFgBcB>dp4Sa9pM^6s(MkgPb7+@dTI-C`yjCNln1;b%Cs~Yq_DwZ@fom@BfN29=MmH;`J0xhRE zJTP9dim(t;8+Yd3TDuC<_@7uM5Q9eLh7=ruh@d7y z<4UgOBR@-`)@EB3Vy=Z&iG*5tgvYX`ZL5=+Ussm-^SmW5M0H3R_|DdfsV(2=_mDeLqz$wSNW>+BA52J zim!2$y6Oqeco9V{<-t4sM9X@4B1Ehhdrl@`?4_c$aQ^_d6&OokEBdQ^JQ!?tv`TQA zRqL-$3&dv89qR-q0qQMXd`$pq#5gt(LwWSh!+`N%~yKO_|lpo&Y-nV=bWk zA>mzY869u1^8J|437B;KxLz|G@52Ol&$_5?uNl^;;ie2^hYp zU#(Vhkr_4?v~hKNjyaA2KNiT8JrA(oVxg>c`EDc&YPMv=JVls-xnJuK0KMt z6fkVtI2dN8>0o{N$x^OgIELJT$wHNgdw0W0>h)G_o}}WW`zUc)1!M7EBZkdwzzOjr ziR#WqsFtF zVp*^j8DWf_uX6qYUO}V)aoms3?;-m8#X96&gAAcPt>!(Wbue^nc(S6|bHT%OU4r^$8|l#_?W#Y$RjDk#*p4!M~w!rXTfSz1aKv2B02hXjnfN369p+er3;> zgZrR~S5Z9S5sm;6u}8dG%AJ{le_TM@Q1SxVPd*1#)6!RZAQ6*{K2d@B;3pBO z^ZW4-35V+EafoP6$04zIzRXFHm^8127q$VgBH*8nJ}3UFPvX*ys)N}G);?4ct*KTd3@QaCumePMuEaJzjWz=JFc^0%=*@fh61*ne7W_D%a3 z53!e za|A}8q)(wnH4lc)_>>(51SbSSE9pZ(0CU~Tufzs%-2cx;8SnV}kph+rE68j({YU%{P(^C$t=GtYt6>C31{XD`{)VP9uNFl7A*2D1u5vkBM5Azzk*>#%!U2` z-F)9~8&{TQkwsZYtddMoa%49K82+#=wibh>EXpAgCocVPTtLr*qVyu{H?-|e2d`RFIELJ^%N z?BlBcT4?@XcOIs9X7DDXZ>PHErTEoRI_*mb=(y{&yIR_(gzLLU?~J&T_Ex21%fdSp z{#vyolJtIXs%O5q=IX<2%ExfcAH0#==9FcQaZ1kzk+*9zPC@` z+`7Yq3T?iwRem&4QLNlq{W@J7ICp-mxN9+1CAbzelp+T{ph`A09x45MD(srki^%}} zq294NF!Ii1F1^-)$D)Ugd^+%t?7I218ovePkSuN(>@4Cu4JI(G!p8vM{9J}7GJgNnU+}A3_-IO+Zs(H_Ke%ll!4#~H#*YrGCan8Q1o8ASK#+XqHC*J z&aYe4X6}<`EdN6FMK9!w%jqO9_yg|B_cS_sps>I1^i4eRT-|1`LJo~n^UI%%_}uKI z@AB+WZ_9>%A{90mf^HK#M1|zLhMW^5>70)92@TqT8o!78+hmZsU!!7n|;G_rar; zRjlFCk7X{Fw9s!}tZ_ITh(~9lrj&it{VffjTY0>d^AYr_$R3&trTFnstkT?AIqf&F zILwf?WoctX?+)H_yanV!-Y%BKk}=85H$$#Mx!f%DN$h*2yC{XGq=zQAPqop{@CX|BZoc+eZP~5USPjuX`^je@ua*Q2ws(U9_KPqij?SAb)^68OzaBdouS+F`OsO+ynkBjjxEr@;O6?SNGFY<$-!4>J(t@${XtBk z^vZ2-auzQVte5H|zF+6mu~j6?z=^fhNVq@ArRVAVU%xl%zU4ewHG}?BuA3{%d+wqC z?9CQST?2T*l-8#&gnI%HH@k`*Z^zD3ZheV*MuLW2U#YudMt%hbkK0wweiIq-kNdx^ zWp+B|-tl$c`rljZ4~G89#-bY9d1>XjH*==gsw2Zb%%jMk0N# zUf1e&X~h-nV{oCu>-yN#<{c|viT+)b+0N1Xu3|Nl3M=%hl)Ztu*!W%ULf}-*;aRRl zCjHuZfCxF8QmFjHuH}iuFFbhENySG|KeKV-<3RMS$X?M~xWRp+9faw#;8ZoMBzn^E zc>BjU%=Dw_y9-_Bu0mI_t&wmZ^i&xP))CEnej$TJ4$ z{a(dPC*AZ)k`M7H^3(i4V8%6DsHiKsaDUIx{Ce*8lgvcVN`cD&@GT1Wfs_s1|1y0( z>pM!vJ2P3;LN_o{AH~YrjcnbN`*SgP5M!m&5 zl?o421{s0*OXd9#ST5;otLFGMnYsGS)6$u9XT!NCtFQa>6^RU$JceSwfI?C0#@-J?lYVQWwICbYpuuc(J-e!&F*k0Kq)SAAAN_ z5?$zfblcmBIL7_B@w%^Sj?sZ}M(Nc_(F8oMsq!y-gl+kIhzAcOmW1D60*<>*P9CFex5~ zFf;B(CX5I^ zIO3?P+cqSN>7P6GqB}C>1IvZ%D#eKA+Oet(%G%#%7%<-W^rw@-LKu-Nml2BlEdBUVp!5sdA#eJ`kVgP#om!Yo2*!{s~?X=uJAG*7Kpsd6#SJ&Y52(edH~{ zdWXkyaCrVnG3`ys_iL=_hHTb8*^d0O*T1s9dwOV$*78(VmJO`UL~=~} z$NsDN?cR=mDC}yhJ@4r5?UqC`P086h%|UNvEKHZ>>_Rj)BOMfL-VgjeyuNBpbJGCPOl8IO05}=$N7HkSPus8mr&`j=PM-} zOMwDZ>rFYrYOXu+;?EY*JZFrD@)knte;1HiMu7o`Fg z4@>a=>wIW4Yq77Yw%`wfAUt4?d|+9fvSIzPCqRmN!_yrgR&gEvTXBsb%X7-pNC6Q9 zpS6||BJzKP(-l|%FJ?G80McFGheJt)`3sg3TB@DztkQ4z2RT>}ur_7L`wDsBhSaqc z_oTS(K}mWY-xh6BO$2&u)>gbGOS(;2DW>sfmD+M|kP9SypWC4#?s5<-V=3E$i+f^U z3HhdxA-?BccpYWIrs)L1$i!c4kB$sVOPps?D-Lm6VyJy>P3aBZB@)@{1V*M=mu$z{ z9t{;B#Wa48fM$mLpjyf#KV9 z`2k|Rrd&=k?kw>_j2xJW%&UMe;dqD&rXK-0=&y|qRDEbx>RcUw>$Dgee>8o&=!M(t zBQ!B@gS)$}n75a-KqRAFj)YS)RvuWgz`#|2OlxcYo;LWzd?=IQOBy;pz(Asir~NXS z7qNsRnl`|}4Y>w+4GW@E7a2TZ?ceA{%8x?Tyg!zreahNnq>}s)EohGHCNf1JUUjHb zPqC(<0B1caB}8y#HONpSz5+Yc(3+@^j+&XkMOTt+u4iDG{*U*Ij0nImZU})p>3{NE z_dKr(FyxDdQFiL8mBdrN1gx|` zyOD6c4$vC3O6-9oPDpeQ?N+7z3r7icB%;D29!WyLW}Ou z-L2zPDD?RX5{#iOayGr2nQ@!KFw$>vF{2dYmEMT?j})4b}$fl zYIZ@cVHBOfNzYd{vQ!-N5Y|L0*1iwUpy+ORhOo4QOX5*;Vi|Z_Nu;9EbX`HtzIyu6 zj+fj+Ldk8H*l0l&(aFn5`c!M^-3aF6N}?yZV8e-6j`3r=%>4nS3P#7-+GU?x1V6Qd zwH(w=_Do{+j|_Gu@4;C+4G+ z)qr9(dwt}B`(L3Y%ogw^2FWlR&}W-r|1~3|>c?JJAEO$f097E(5m{R5{>%8?4twNWxp{joxH3 zrPOR0>)8tNvKS>FAw5&4l_0tV6u0)vs>y9~BD!D2%D3i>$TlK~X0D+>asYrl9HSbU zon&^&s?U0uc(7N6?jUoL$92B9y__H{yy5-2osBMmoow{Kt$VYTgC6n9lQI{$G zam9BknGBSvbE_GSgcdi`86X?-JY#B{+W8@kgU&3e4mW;*OhFhWP3frz`Aiuk=01rC zf*Vj zD|qG~mu{uSCCpvW0hG`r^OXzit$#;!3L|gs(y3ATe+jKKV#0*~L7@*tLp>65S8FH9 z==E_XQv$vnm%hVMZVOfi~E?_{S_yY3n!_gN3fRBX_VQa$YKBTo3 zuQCKFxM6=s2@tM|lm)SfvvdSz3AuF97T4asVNK}5gPOvd(9dCuuy7l^QDhiXnv>h% zGoU9(XYdEduE+}>Q$S{8$pb;j5qSS;3(V<`pQaipct13F2YcW!B zREURa9UV>S=!Q6YcY|5r{}m6td!9|A{POViBgw!X44U^{9le z*?s)E2(Y;fki?+d zWoK(6k{}n@Mb^q{P(^Fornz}K0S1Tj+K|m*Uq(T?diYYBXR}JtBW++!jQ|mF1Vm7jJu<4~CWO%V{L)k6D zab)vZ&5iY}m-3s(DC*(jj-{%2A8`vPGbNhOfqL2%vmwVtOe_E`)Ku*KKVgyR~HpM*0B$rOia0mZDwC5ND=C(>rv|vV2=2P?{b3`^D|ueI(sk#xJ@f@Biq0;Fo=D)j))Xe+n_$I0UjRBBCt#8zygkI0!^vFl^M=+nanA%0Pfl-3p3BB@-7LfE z!L~82lhC6h9Bf87UOTXfJi10s#gvv6$OZP16(nA4&A*3(i`Yb^P)S%CCL4%(QvvGbk9jaR#%(Ut@Qc7vQsHk<~%&MNvF~3tzW~q=PyO8F)s6KUK)% zapVM2SZg?#@qnl>u&D`1z!LG$lhmn-;G9w5zlxk7C}Re<$5_N;Bc&k~CUE=&>~M(W z(3YqouN>k8$l|ns59$|;_odz1jCMy%5t?8#u$u)jI{}6$?4r6D_*^TEw?Q6AbW)9L zws5Xihf_Fw0|kRXGR$B@hYcP6)AncZ=%0djVBiJ@sH*Dg0_-K(Qn};UM z1;7+s6wCzu!M+6hU(x_==uH@0e9W~=O&zYf{}S#%m$~6!D3?WXv&5>*CmbdatSNCu zI~+#%JOmaWEN#DOjFGKck_aXs_8+F`KID(-710WN`+vol6a~s?x~?EOd9b*?h$5hw z=wtzT9>^#%Q5z@^PP&5lRVQs<+$jaO{2JQ?T5+2sD%ivEE3e+k2w`D=tq-oh`S62&wC(DCruZ#eV z23yj&jrJI?hs;B3pA9305wz%83`Rq(IVZ+&Bt9NsK4U^oRJPQ((2`*R9w4?bFfb0L z6hS==&%=0iAuSR7j+%PhZ*$|i3g*ZSv*q&=p zbvtEM9lN8dAb`9m>b@uxZuX&{WdTQR_{Ag$zCqVI&>a^9g+0VxSP~I~FPPe$0jQzB z8B9vFkn0dTF)!Odn>SiG)2cNAj*Ef-!{?|;JWVfQ*gHS-#CpezDXp2M8=mMfi03}~We?U!a%a}ZoJi^T`2XI=;C=*Ef z1kNXhEp-8AJ|kS7UZT&2+4A#c1V_6bM|9!>7)a{_<5SSdN1aU)4*-Z5k-lt*XK7Lh zb_aAAg)+b&?hF04bk{|f1iM4Qxh*AM(fKYWmxZl_ue9L|u{~fA8We~^dsft(<`vB(fH5h5s1*)N%hk}!k@$)6bqFH+5j=d##a{sN*Hz)F%`PR7IW=Iz=lW9A;jC zDT1+jWUw{SU+aMMV+a1iBv~UZu<{5q0qFcd2qENv@VF-<_!Hm+XF%<(R4nRIOsu0F z2*iTWLhI5v<77)yfBM?Mg%Gk3rGey$Z5dIBDope(ei}=XfK&<5j#^zbz}6BP|9#Zo z=m3!DI0Ot4fiLtW)X*Y9FPr_?0tEEcgz+F-16YaAAC(!fbpXknIOQ)l5)H9QVr%@k zJVPzn|0EC&pmFQiUwoh$?1W>@U$naVv-tl{Yc#wL`5^^#{^uXj;h!(~7oGn9{^$P% D{|Gic literal 0 HcmV?d00001 diff --git a/arm9/ds_arm9_hi.mem b/arm9/ds_arm9_hi.mem new file mode 100644 index 0000000..f4522da --- /dev/null +++ b/arm9/ds_arm9_hi.mem @@ -0,0 +1,11 @@ +/*-------------------------------------------------------------------------------- + This Source Code Form is subject to the terms of the Mozilla Public License, + v. 2.0. If a copy of the MPL was not distributed with this file, You can + obtain one at https://mozilla.org/MPL/2.0/. +--------------------------------------------------------------------------------*/ +MEMORY { + ewram : ORIGIN = 0x02004000, LENGTH = 3M + 512K - 0x4000 + dtcm : ORIGIN = 0x0b000000, LENGTH = 16K + vectors : ORIGIN = 0x01000000, LENGTH = 256 + itcm : ORIGIN = 0x01000100, LENGTH = 32K - 256 +} diff --git a/arm9/ds_arm9_hi.specs b/arm9/ds_arm9_hi.specs new file mode 100644 index 0000000..c5d66ea --- /dev/null +++ b/arm9/ds_arm9_hi.specs @@ -0,0 +1,8 @@ +%rename link old_link + +*link: +%(old_link) -T ../ds_arm9_hi.mem%s -T ds_arm9.ld%s --gc-sections + +*startfile: +ds_arm9_crt0%O%s crti%O%s crtbegin%O%s + diff --git a/arm9/gfx/bgBottom.png b/arm9/gfx/bgBottom.png new file mode 100644 index 0000000000000000000000000000000000000000..c2dca197cf04340ed596128a552031fef5e91f12 GIT binary patch literal 36593 zcmV(rK<>YZP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+ReRNlH@wFW%`sh4)BK)|!(MUjb>)r}gwQK+1|JQf@kN@#Mo>brWdQ+*n<$UwMx4kWNzNq)_ zfBpRV-0%7G@Bf}ZfBwt&uYY_Q_&f4<{QaQjKd<`x{pVjUb3dK0(m!3O&ObjG|N4XW z{{9a2-!Am$jiPrH_+tIJQ2TSC7=Ax^{#>B{9_!AnmVbVPv44NBzYkh}|6cwP%enSH zF7ogH{T5blCB7L$*zt)se}?$}ZXo*abH&aV?fe<|#oyN<7XEy>;O|aYkpHw!_dng$ z_21o<|JA0%{QLgJH~w`o^FRLnkKcdNe_s6SEb%|RbDaNp=YRQrf8zZ2$NBs3_pN$; z-~K&ae=|#(|2)*+M>${di-RSP=Vh6H3jZd)F6W=dKlQ?TC%d!mc@O@XPkRE}@;&df zp8kB#K7Y?=a*J=+q40MIla2CwHI~rAjQ7Q6zW(!kp|E2sR`zVNy^-&Rf85J+-nX5% zn@{D9*TZjyhl@SDB3%CQf8wA2<%Pb_wSAtu{$s7Uub`oF8}6Kc@>^`k^Zm7>bQbvU zKmYze_cgME=`3^Q0sB3Eml)~!V_Vu$JR2SP#l+t~w|L6>a{-o!_fFi*6WM_~KQTS| zxl0J;d05BF{&X%>ahfpLf+wZ0xXbXynAw~fd~aS;p7XmQh2OWqEH)y=j3ZTDioFup zu%Gf)Y^bN0-jq^KCDqhY&oRF_<(x|v{CbJyEve*EN-eGQ8tYqA&9&5e)mD28Edq|I z|m)A7RAtjx_QpqmDNE#C+yE)6BEXdd)Wb3M(#fV&zp;VDb)A= zrZ31>%Fny@ntAQQ4b}2-!|x5;l_yN%@~Djke-^LJ^_yd?RO7wd`xM|U=KbO9--khd zwf9}}4&N?-APcdvrZ4VZzNc5dWan9^kUzkc2wUDA7K{FUZI@M8rgARCqSkM4$NZ;J zMRcf{tU>N8TTS4``P-yS(}-5bQei5lSgTjKYHz1ie6$L}tA zrsv*uVd9lH{dCvgeeZF#ePbsq&v|GTV6|Ry-UPEbSTpwOc46}8)$?rM;`_Yy_xIw< zrH@zLft#M#{CNjl<4t|XUfK%ZDy8*2p3&IQ#cTe)6{hpCRJ-NL57L8Wm(l(#t4khO zE$(6t40)fI&6J3DJFl||^UG(s_jBI1v$?#h&vX6ryKsklu8xbwoxa`q5!VTQ@(L^@ z-s6dTjk5%G^qtGxqsCdWwd>DzY7sMY?TEeb=40o2NBkt{OtGW^hQxIT*QY&`WsSYA zuvdFWn5|u~hA&JYtFps)=~((qWfY(fKUv)O=-=FVgBYV?3j4PdIpyTcEG zIh<^dHTaiZY-NTITFrj160VsDGeo1Wve|E1uoVL7CkA> z#R5MW&I}atAL)Ieu`6JY2@tFeb(2ac#F?4)bMdX3G~qgNXLiM<>3dbyC$%rLXlMc8 zuYquE7I?st?oF5hsMtrMFHYAkg2srEg?Qc%k0g{fe4JZjHC|v7u8ySuvfS(b`X3wu zaG%afe64~|>wv$Jk6kM(Yle6f&MN_0b-!b_d&4;5%l$sJ5%9pD`t>H%SKf*hg_Nu@ zFYXgVsuP@-izk`2vJB}hd^m<-lP*es=&*{~+Am$(4RHOIO^ zVAgKD@*T+A$Pre;)8N#p^6L5BDX|6tu6FTwwXc>~jZRo5ihQgWbRu_F_FlY{+xx&H zFz>>GF{Nx!cfWnpgp7FTQ)r*d&C-I)_ zQY;m+F@XK0BO#GsD!4HYb>JkHiMG3;J|KTeE6r^jc`u}D!b;(h*mVJ`ZY*JA`nY`T zUpfo>u|lcL2yVehN5G12mmYHQPC^F$aD{%eir^(-jk~-t;fC!jEDm&QPa|kPxH9kc za-RlZ{0&OLO2IX;D(Fd{_eBKbOZIxb;LGZDW*j8JtWif;Md#-mDNIyl*&9S0?#1o; z_%VHH0lyEfbLGb7W_SZOBL@mnt=WyUgwh|^BvM04fc+si8F(W&3q(G1%oT~d5X6g{ z#7B~X9s z`|z6^KggjCJR=M&CDsQ%dTrJOH_ERG16&uO0f(!ZmE>(pdx>Sh?}rJ0u@DP$W*^gHW@abE7_ zE0F{P0k}}yG2cIpq1$}V_*kBXCTzq~Bunc9?gpYg8O%dZhP)u71O+w{>T`hOTRfDY zb{I5oL{vbajkZyPuz2?Ou1JvCh~;0&7$lyIH)7fQK-)JlOxV{8)rUF!$M0edk9&29 z1135u3;@1@Lxua&uY3+d&Zvjzq3U{0L_rkfCOh!vba}7$IiEz=Bs3*8tH>^u|R#p#ex1p9-b~ zTUY@i0BsoGg9TxvC^xhU8_X9dHufUKo!Jl8_I}Xf zlkI)c%yEEtqs4eV^C=dAp zVPSVqiJiBDCILwU($b;-1pDi)f5;1>Dgyk|hyiLnyzBF^@c7^m?4b*=W%ztMx3<2! zV2Z3``k?jL5kzT)Fh!GOVCxC`R^@dOcb@@ot6Ak>)CKw{z?tKKr9&?;;r&jws9xL{ zu5RM(WA@&mS`_l8HXJwPXdXhUXJ8j291u6E)V+wyD{;ATd({; z#|is@Ii8NHnE<_#1TH+y!N7B|{&|;M6)%kB{-KE<400nHy$(zRgG(Psf17#gN;E0e$;%rOM z&9Luh)sr!}z$^kQZ^)W@I|)Tg;0i38;hRn3*Wlf(HqKCy0tg*8o;8KWzR8mc{z%u~ zFa9@w<{ieUJNPSBUS#0Pds8haLjGAr+3)i3&U^y9XWaAw1@p*Y)C2 zKqMeipMp35&UvI4=j(#L$s^DQ9~je%KMlpf6~HClxA9|m1}L5{=|7MIIJMi?M%9p#SU;_+cFH^R9T^sdRwg@4E$_q?H&5D)w=5Nui1hO~iX zbLqr`@2J`)TztER9|+?Eo*!NGu0|}AU+eI&@_i9`3GbMEh%zCbtm>LbWX#nxKQySY&%F~O?uU2^ zfpU@4xuXZ{G^pIy`*gWW!U{|*8+EIyvh%!XSmG|xvoG#Cz-r)cteh-VdOYs{K_M8z z@Gr<5NKZf{=)fTOJwSMvz1ko=^z0q^C6xJaU^7}1Klm`6^57xi#mXOGU|yY#Ft&p( zb&m%z$PzV>t#76DhvaVwNR-4tfcoJeTo4TAF%pQvZl`)*h@BX<)!DqUfn^Y?2xQ5` zH2#am`q3+7Au<-@lM!@+Gg}QYz!L*B{rV*>?~e8PT*V(^xRLwg#vq`Lz&s1232QPv zMXW1~|sB;^ec zI~i$T0_uwR1rS3dtBJevgaJ{-5@tl<-9c00Kqge-U^!^EVD5~vv%)P^2q||IjZ_#6 zga8vFxW#ZQG0_k&?%@?oNdwt%$HDEa;f?%aNhC@Tq;tO7?=f)36w>pVdjO>Y7_(v@ z_pe>-&xmuv4FLxw_h$EhmJt3%6Qc-b4sP*osM!YyPhcLx8J0~%ET9Npu=WWJY)3VH zjC|t0HnvY7;2!UJ87#vmiG|#NDG(I^M8e5DdBUyK4PrKN{^{pD0`40Slu)!8 z;E+}Rl95(W~D$m4)l)&}6d za5&L!vS4@$hy(<}NG8vnUlS1Z4LeGnE^d7Vcpq*9I%BF4lGRvlc&#TiV{Tj?MFu~D zq zPz8O@0+xh62ss8o9G~ANmjl5N0Rothxr`4li>)J|B)J2gBW;-_(ic7~3g8DBfj8e9 zD_0Am=Sou$Of#-nrnw!|Nq?_IRD<&0N=~@#az|0+w<*^Fi->~a5GrM!yxB`Qf6cmJ z2W%k-iYgko2=1>Xf^;b*yZ{#?>MN;GK>K7)iYA4Z!@E%q*}Paj_JJ5+xfXj@E+__S zw!CQBe1i+DubNP|KS({m!;E=vE_6?fmIvno39foar3dsHIBXY7jnYGv$-m5g!8RuM z%YMaVF7@W_^>Kd%NOw4Iq!2G{ie26>P5As4~T(%3lfLG{8ys^o@1d+$nHsl`&>=i0i zEHyB~vZYHUD;?uo{J=|w7ei%@8s-k}tYvmVYlkB|7RG?qLCJ)U*P_9lX)#46p82m<2< z&wzd4;U$zRuDRcuNWGv57ahQ%u zk2 zLz)9mQnKHnJ+kTcSf?b9J#&F*5ugB6Vp0Z}c=G-}a-Yh3r=yRd;OQ5!j91w8JOStz zuZZ*wH2@hs0qc}K5p6Jufm>mm+l1x!3x42Kjj2NsG~fgT-kru^pq0Ws+69iwb%5X=a! zCCrc@#ykm50z2?TOtJ(vC%m7{9tkgbPr3lp#W~F<;%$9Sq)S%$NAv3lY51!W+0^9W zlnG^@BJ%E1{iK(b>gT8n@rMF@%||5U9rzbKfs7VQfRl1k05w(u$zlZ-gfBO1*& zaqvI175DO(>-9K0E!PI7f(haX#GO=l+luqxle1n#sLCDp&tpiQA+o=VfHhuEL)RNT zwb>umd5RxdFA&Jnf`_OL|L!knp=B?GQR}GqcyRS$4?noqgSiTc(T4^TalHVIHOvt; zxEo?_Jc1Af+b4Rh5#NL1tm6ngt*6)zVH-MI%y-RrOc3>TE(-*C3cfxx z+5#TXLk&(yBvq+kB?$D65XNV<7evvuOPMSk2wGTX7{3SCaS|c+L$nLY(h+$T(eY)O zpa!aEkJfWo+c?0Y#|#_ktO`Pj(@J1Q5fW}Ot83p0J#FD%(CD;&Ff0yNITq^w||MnIGcVMfD!0+A|5La z8gKVv(FCRCaq&}4SR~#FWeR^J_ISb#DBKY50GLNKwxTqI3+|0`r>HV9ZS!%(C0t>I zVI~W7k%B2y1VuGXHlb^{3X*8f?hR(w>Ak|Pl}n%;+^icFyyI>jTm^s=#okdHJe{X} zU{w#+3gEOn2yqW5dOfFZ?ScaQ4*34{cn^SsmEfwDeZYCyjC}&>t_tarh|K`bw*HHs z)o_Jzqd=PBxDr_xua;wp6B}*?U%dY*uC4?Dg-|{~OjpZw-j9e`+eKtzAMaRoB~~Bn z-Z;@OBt~xifqhRx$~n^M0^GvqxvBf4ORXn`fKTN5K>WbnU;MCmvJ{kr%cF{lLNRm{ zkp| z(%Au0u#c5B3h&<#h$aQwaVHBKc{N-V+wC{&xbO@D0B-Eb(zJSjRd?;Pxyblj>`Rmu zoD!>?l?_pv74eWe)3l9$COdk25zxvZK5fmX&<3zr| z^WZqRO!p1jl^!?&OdlSR@vUWv3q)~bGt3)qUH_y!i^V{&y@!ZhVIIm?plYCg$5`)m zs*a#eO81amwN~}mNqYQnnXeF8J;*g9G^59oLE!>9zrC)c zCEyfh?9Av@2y{1-;=utpxi7vL9;B1Jnk@+-EYhSqMpBA-YXLBvi#o zpMvrU4BTP>Axj*_Dzk9FC_&X)!jdi|dR94G>Q^k+P!ngw=?88C79L$$X$x;s6npk6 zCn{4ZL5Ks%EuWv4dAfnsN}#O1$#a{jHRhrR0>lXwfHIzzB@w@>=Zmo20OTJ-wb*aX zZh`Ix{(b~skD0CP;*Oh{KaER(1ZXtJHZp?8_>4M{&b};*6wkYCuXp%Vx=8ODwF`4 z?UaVXFbO9r}i@3q;d@|k4(tbNWVX@809@=IK=>zC^G^!$`h z-VKg=h{|bP%+IfMIusVCzww{ShJvv&TQ7SLu=T31X=Y>@h{-}sYhBcopOk#>`D;@c zpE+tzoKeC8!$;gC#^R$Oa{z=cT+mbI_DL|d74(OdzaB|Il<3@ znwbSQq6~;sxX>LRp4VDoVC~sv|EA;VaMm+U-pGSk8VLk zKGwLP@nDuJRgc`+XMGpLzzQek6F>`-8_67JTj?76oJtOOaM^Z3m>nQK-%%8eirOLanka-~V&u>16Tn0fN$p9ySg5|H?G{Lxp;kpL zgk(__o`gbgh@$5Tilg_G9FGhw#&0HwYyI*#nTOBJA1zlh1;;cszIb zZS+v()q$~4J)kGN4Q~hB&~dm9w)YUtBS3GV78)PwXUCx>QRaJXcU^$7J#s89yyC#d zsT_B&+*=}8lgiFAz;8iWO3oHxbkD_IHq;#u3xqTiH2z{%VJDv;g<>G#uj}S0@>o~hWpAX%32&J~F%5O(`XaB)xz2f}#{0?AkjB1Q$4 z?S+v+$~{w+kaIZ`fh(S?{b&KA#`;&!x-Civ6D>247$CI$+rAELYU10FkHzJ{BnwnN zrHPSgei$#{Kdk)?I^@nWg8om%M_;O*XJXt7^^Ke04<8}(*W8u7tIx+H2n1{W{_Br^ z-}?98{rju`q@^&vd!jTu?L)y(2T~-rsM*vt#GLFb#t-9 zI_JA`OAB!G*4$ca>sv2xQMU!uqBwPCDnHd5gd0GZvOLflx{XO!_v-hia;7GiwQo93}D5KwT z?)!S2SGYJKr1T)@mjEY>*rjKJ+uYIG;L(fI*5iOT%Z)#rMoh~qq34#X9?6OOB5my4 zXx#r#4ai5j11nJgMv@K5U1F#P|NJjEF!yi;;5Wrub!r0ucl- z%55Wi2-$fL<4wd9iP|AX_L8_kyim{dJOjavwaFC&0>3>fgN2MJ5h{|v0x3tpu&r5C<=4<) z+568Y`aBrf{XicOGP^@&-_7c&iSJ-9noaP-nB6d~V#Tv~hGN!JkEH?jG zz_Jmt!*Uo_;*hw{#^6>is}Tquxp?%4HGdWnAe&L+?S0K%q>LWr_dP*|?!f^M@a=(I z|8+ywcaO_<69R4je#{FP`|t~EeBf61GK=AHiN$fK&S9t2<_b^|{9$68HdqlXE+C4{ zm_*OTw>^p@65HJ5goi2!n!8Ytly>&V^09TQzlR;{o1ePVSF?17dzkhS5tTJnA~-kA zCg+o%N!(~mrEP7-)-n_UOF@7zHQ;FqBL8BY?VQ-~fD^l?rG*pr*dRhkDb;zaxB*D(D$Roe?>$BsfTOaPjY zuzJeM=?09f`!(~qFQ>7D4dCb7_R=;^2Bv%BpFUJusL#VA0~su!s`M90!f2m_1{7AV$Q) z@nAOS@omG#)5k7{_oBxd+9V)qjCsK__cqj+r@-4jtp@WCmdBX^vIgu%!Bi=;h45eZ z3%-Xu0vGz_tA35t?A|`j&IHu!D*VA(d6O4d?!L&5b+@>nnm zc+|8;8!{64pFVD~(tlZQsh}>oPAzal=(1g<26K*4q(1{)O zj2gKi+K}G70@`Y!xy=LwAtH$MF9823h%d#sK1zr*+iAn2ejt-tN;4f>aYLwy?mg)$LH6+$Oaze3Wc!< z_gfhk1*f@cx61Ny(U#Rx9yk;aXaAnBi=%|bkJxXAa6bnC)+Cx4)6;I`arfuDbN#*BEueQ#%vfU1*_W=4y6fbqc(>>Yse^By4H{){NO7!DzG zmPWt>X9A!1`_`)sqSIqdPMTmjp%1XXd3a+s44UPF6is_i-xPQuQXR}O#aR!$I5`Fq zbpek_5r#v-_6i)B*wyT1D>5F*`XSG+W_MbX(4>yyAc}g3YW~Dvd<~o6%@(1X4G1C5 zE~&mPk-RgJwnG@u&k_lB>{~2@3;a?J9)xgb6)?v|^4BWsy6v-9JPezjxA{oVOHrK5 zlrQi5c?t*x=~c!NJ$*X!CYlQR3ilg86|~Q`ZI_kjZMn0Ie|CH#Hbh4xQq9*|W=tN4 zMGc_Dl)fsc^wb8=wii{sUyGIdqfGE?Sj7x_H~63#eHl99xqdR6#q*1tL+)xL%A48p z3z-g^1)_-YFFfzfmOmmUJ4hzv0?7Hse3kq4c(6Q)s1BP(1;fgo&`hUW zZ=Yj@)mqBh2JK0wyyo`6Z&a%)EkMFQz$(j&(2~Z+YUd&3!qfe}t{0e; zkXaV$X(aKdYZt`<2v)zzl<)WX%OM(&B3Q#hEHJb!1oAwZa$Obs%XuD1_Z-{_7!VSx zNgA|poHGETbNtrHH*IM~JLOro`Q}uw|9Y9{J00l`B7wYr)@a#F=!}3B?W3yy>8-Ex z-bu2TJz01ifh2e4BK34Z<7EoMQ;GJ^Vdz`$0K9=b(M znKc{{oohDVA{aOt`a9Tb@ZJ*<=X5`FX15-SmUs~9zQC&UV{T}p8&bmA9muIPFWK(IezD)tE@0b&v`8meDIlgS*YS*Bf{967OQyvW2&D`x-Vz}n(CBOd-6S-?Lf;@Uq`}xSO$U09toHx(%^ME zMLZ*V)}JD$Q^IHUA<^b@1PMNzPw(9sqViRNN;idMpK|g>tl3 z?Q;GU4?LaO0p>eP>2;;U=~)9Qfg(lVkjsp5TjG`?aMoX95tQ}?|KLWK!v(~-VsS6- zlWnudVS|XcKmwEqZjtWlVPA4?#$8jX=e=x7wR;!Zd;M9~^poB210Nj!uD^Pk_)C-_ z>RigHI5IcO&Z--$9S@d$&T!3;;u=H>!UVfzE6pj0w3#QJ^uL3Q?CjcWElV!8nLr2? zoXcL44oVMHwm16KpZQIA)tP!{W7zLbS5wG??(4CD(uaXsK)cV-n0B*?&sTB|Y`=QK z+Lx%kV>Q)iqvqbGcsyeCEX(fb_Bv4@+I}lA-*&BXV{Fm=T`(~=7RB;-0F4qtLAMcX z8lHxX$GISIQyzKdZZRHlBAs?$D5XyOK+2wcy$|3TvBzfVNVF>5c`7CjVk*|x-ws|m zur6OFa{rmsq*Xe!0;)Qmj$ChUvprOWOHB{LQH;d)#}N)6u=3>79Esky&3A1DRr4s( z^Bbti3!$;Ymso098yf)E1PjEubL4fO=1wdioe5giDJ_<=*rao4KE_z>gKU7^&yKLT zhrnqaf)zia798Pz8-UZqeb3J`8srm1XW||a9MuUj7Ujc{!=+JqE|hEreXS2CRAE@y zc6U0*NBH?->qbK~1Q)Szs-DS}*)b`wO!LZLAe3_4WB9LHeGFf+8rB4kE z|HXP!T;H_9F=vA}u+pm~Mvv#>$gFqf5ys^Vm^A)OVZaW4Mf%8r@(#NnPmO76dlF#a zhEAz)o)1y2I7WppZFq|9BGBCl#k~%_f$DAUPVM8UB@?Mm^ucXx`mffnSDQUOGPA*F zpJ{1)`zEeMJZG8U2fBzAEzMJFY)iUF5&>!P8|iSW!K|gldwX$~T^FH#hn<(|6Uh#w@-r`IoFk@o98;sa$4XpCy z1I|HEgRecx326yb+j6y+VCk41&w1y(evO)52D_{0I>=*3;QhJGZ~2=A?9PAtO6tFd z`F@k4d?$j!H6w2YD}(}B)NaLBw;na#=6#(qI9;u3uXp^+INBEVkGDCinfUn?;oGB_ z_x}j;$345{yX@+e-L@*Q{&Ep36fRW9b0O$PVQ~q5h=D}XBQvw6@%yQY43&ZF3 z?%+(K?I0cJU+go3f?c$wf_-F}3qWe&JnQag0cRnZ>-7EGWzpuTVjtlSU{CgV(6sUM z2_Z&vH=nKbuEiQjz-dDPc<6a5r?2XSWA2iTLti=%afV?2p(%!-UJE7yGw9~G zKmo@#5)U8KcmREwcFW-pmtz-TUNHJ`^D6%G=nn~>z6|#=t!&H~&O%xPmnei8@Phq!yaSoUiCGOS-KKi@C06gJ(e|B|-pmzX|N3JH&dh!seLO1dKO#o zk~(jwP#60Tk<<|ln$NLi=mEEA)=UDXCPpkH8;;J~Xq@XDUX17Dn9Q;Ks^~C79vM}v z9N;BGfd05b!76-iyukuv3zNDH=@D8(v{|0v#8+}GdO6!<*@DgD*w>oOj0DMu?{8IN z3BPXr*2ny_{T8@3=cL%RVOMXGTsUtB#$NX*<;2IOuP@5$eydRAV0f5@z4U@>|h#<%aZ%g@_!80 z3S_a1owTC@z7jxzrDn7kbFz5VIC0roFA{ITnjBt%%-nTFU->zxW0&6GO z0-J`7(u#ayul;qWr5jO^ToWPRu3YYPp0R4lX-V6$#6bN9(xC_3<<%n=185-ChAjvI z8^0YnQ+CG0P2GV>XCmA?%$pd2D`ZA+`+om|)%!X=Zp?{~hnh}I;x9#y!>JA1j*CX&0WHOf`y_nC|Q$wL;rEuRs`Cpifxz1Wlp zEZS>NNF?rB;Xo{SO$M1Bj0lfrwV@UooE>RlV|Rw^PYcURDfZrOhPdE3 z)q|hYO!^x$TY2rtsafsa0odBCo?fNhzhSe^Zt!eUqjb=fbye|z#r_pJTDt)roi8nP zCdzNACF;h~XUEM|?$%C;^gzI2f3joeDn|Z9^uefunG5>~zbiX<$*DEtQ8_r$;Wr)9 z0m?9}@;d7PVb9gd-`Yq^yv*Z82X~n)Y3qW;)W;T{Zq++#Bw5Di*jd>x_-~Gi6>=^} zbQZ}z-8jUda?-ptam_c4>qS4Ou`ITCfWw))!?NZH1|_!J>gC6Fyd>%VdOIU%94zrB zGa68-B}c#FeuG7x&YE~6W5hUzAoyVk${8;5rVNCI8d#3v1lP|4miZkC>Hvxdc)wmB zkafaMu!K00b5bo60+FkwWp-kjfNZx#Lz;;*TG-B0BmobO529#YNNdOJ5=i#bwPI^3 zT3Y5L2|{beH)sK;F)l5SO~z}#{i&04V3;-Qb6<`k?>ObljXWN&1JoOFgzjFCmkc=1Xj=Gk2gEj_%4ctjj?TXbMYb(eREmB&Y`K_3RaP1|j?pb$!NK3}<_s z&ykRUA#R>xlaHCbE1Yp*b|u@;SmT*hdB&8R=RZLWIqQYR@zf5< zcHV6ByaZEpPS12c$8d1o`ZK&4Wr1Qa$YA#}Hk+WWQjcX`>@8-oPDjTXYQELH4yOL& zkN{?R$BE(i-#jDO@Ly-dBf%_O2e+U#geU8&S>f&M18;|zsiyTrH$(%z$I`}l+@Ea; z$uZ5J)jw?9`!=WUK3AX8y!mJVy2O1N#L8%xH~xsr60{v~8}Dq5d2|R?2azpVWWUh_ z(G&2?7JjTt&~XW!w6U6_asmCoEtsLf>+QCDJsI?*z(l7^Cz-zi*!M(=W{819P0wf) z!#S{ML|ih5Y(CJUwO1{kZAOUAmLa0QCnD&OJa6Yy-@wTI+U?|cLnn5gZVm^gzaIU^ z{>SJJyh4&RJHoK#anO7?Z*^!cLGu#s6AJPxr)<5vR?7+bCqd#+WHvKlW9ly~eSE*9 z>o6N(=ig^|-5wqk_f!@>6ZF*+(QxJzC(PG5N!g>%;mB)vg+;QCnVAkeWuFPJesaxh z7tEVW+zCY-ep#7+2Y06e{xp!OYDvFk(%RShPDY4e_5R5ogA2A;=iq}Zfc2BkzMyuM zJBz5RRqOVC!;P~ejDRNxIV=>DW!1$ISKFQ$^jq{ic{p;?y4wcO9eGeJys^gRZ(#83 zHdyrvXP9>Nvqra$jk1sW%5%)v1~))7IeaTLizT;ZZ>F)64BPVwG`eku<+VWQZgpNn zkfmwy+G>f9Ap2DmeLYmF3|UcsJQGpddY-+7G?+rw5$f)*1&|j(S%tZ>V_)QAjtB_j zZ2R$XV>{cPl<;huDyt|pJi#HZgmG%`^VPR!-^$8t_v_I-crAV${a6O0FR+K`cJ_w2 zfRiH*<%3=5`H>(9+jWLwJH|zGEKklvn-6sBuAW z0|9?o8@Ye><5)K%4!jkTEdmjC3h27N-*13CXiql3IqC*`wYBUt=g;p-_^D$jxod0O zon{h6>R;u5b+{Z$=qNH&&Lp_nP^%aLikmlOBXC7}w=HRcV?K{#4o z*&poJ@gql&GFv(7pkcH!PB<+db_UjT=#R>4o2&zLtt8Js<6UQJm(d5%ES?HCKZe0l zo$1h5AY;BBG_}W+g7Z_WNh|1aQA3*aa)y~nDK^-l7akOG>9U+BgW@%vr`f|DTO7_v z!Z>iJeENX!et^>R6HrmFItnrU+LUHgtbtx9S9AZ#LYneZz7ns_O#RPh(e2oxXArB^EfzlzF;8;Y ziAN3!;6GnKY{XQOMQ(ju7BqO?-K}`Z(FsIRu!83~^2-m~!4fRo1-7$=rjHZG`KI0U z&~isI^DrJ~gyvJ{x_!!cTGX6|dW^Y##NBlUa7-!9aGFv(=kSt=&u*lF2gNJ*{5W|p z*wBTuY|BSK>sIgGEq+DD$h7j!(!%XcC3X)$+v@?-_sb5}_c9lj)_8?x_vR$UOxxS^ zGyRM@KcEUMO~esmgY^rKQZzac&FN3~{3W7>HOuVH@;bkXdbZn7k8aJ=77tBS4aIb{ zv)kdm?^?umbAZkaJp+{ClTJZroo25Y+*_~n4>wDVP=Iy9C)$AYS>8fui&ideXCx%k zKheyl6@^!4d7{;cNd7=TLb|)cKU1`C)Z%*!aAI{z=&~=r;$H(VF{|w~VcqFE)w9aF zlj{1sr{G|PrZtxRCwq*BH636CU-rafm<~TZ8xMFO2(MzcsGHPCr+@s*j$=%0GBIi= zwQj+j{Xhp%%PGJ3baloLjs?)IlPa1QG`SwiEW-^T=) zrTRxOD(1Z*t2iwXZK4ShO6)|3BR=d2FdN{E+zDbevE&dvGjfjs&4XphA`7y#EGQL`Z^!QlQwuDeP4bkaWjm|e`Tf=u zM5}JL1*u4x=XrwXH{&puJNpQ9yehQcPtvlEk(`pV4n8Y1!AtG5z6auDf2Kc(pZkQI7kP#>^nPK}EdFSd z*_0rcQEBRZ^&Ys0FI??w%2h=s9&>$pu1F`@^(>FJIAT`5gWe$e#i{WF`OgRY6Qy=G zm%3}!2t0efu!f{@WU@g0}Wr<~u~> zif;-cD6h-z%RHU>HFAb?w~pfEsyCd3;AP?f?^lNKP~Vb_A1m>R*|pbhoTWSnaaC*{ zU1*;q!+T3HEF1T_gy_YY0I8$DzH{Ou%PwK-XPcFdocE`@)8dm@8{rGng*S|PmgcvY z-zyzHsvSD`E@eJ_n7LCnXB5OMO={pq*CJbwPx#(6wDl`I`NrLEeZPPE&^sX+ zu0)({xWGhW6`R=Zo1bpOd#bBuHaHSb4#FUCHN8JCX4!wmVH)e+6mv!G<_N}j~SY`xD-8@OH)Tx8<|QM=7hk5nh&RBQV%V%nyjI`WJn(t6M;ls9tj!v)n zrQtm34Dq?l=%Zh9*IxzJILvjO8l=qex$oVGp30hjP zV%So_b;dx7-xrY*^vb^&M&@{(->;rO@#7o$tb-@lA%oB7ev#JJr77z^zUvu|R(@9y zdDPl{tJR&l-q9- ze{2!$ojj7~B+t*gC8GYN2}YZ4Wy~oOT0pfNkoC!`t{^&_^SVvT+STyfheK-^sbeM> zoU3+XTh){Q>8fYkSih?%gUT>O`ZZR3)osAL%xi>{KqhlaCKg!8V1O2Ja_ zS?9A2f||2^#}OmvZYh(D#`Bd2F0y`_A&(W*W{EWho~5kz*Ua2+{4C)}6V}v~{ov{~ zYIQoCY^PtrWlIa#_K6-a+-1~q@x?rrRHKZuENY-M zd4Oy_RCF9qE^d;%@)D0gp6UgOz(|X9)0OaLf|#dIpFE38JMAbQW9f}p3u%3O(iB7h zI}+9#PP~tr;$x*uik(|{Oyhq`0vd@#Tg7Gvva}_KeK~E3PI~31lm9t*XOv9$fCKj- z%Rb)(ah!jif8fNp)GkG|v8ry&Xlx%*3u2!Y+pa%JQZrKd+t2nFZ^U%>Sl1?e7Pn_t zWY1?wFYyE$$5=m(XDIp5!a4-#8pkl&7s?H z{m0%_>0fNkwq9=N6dNYW@?-}2)9V*I(|j6QO{$$VV`tX$*I7}P)Q*#`=jXGH^qlD5 zv`ynCztkAB&e+9CKtWTa7RZdS~jT6xiIbSIPY$M5*hv#0`5e6WfvM{;Q&A~?SzeSW2C7eZR6o4 zU}fuJZ71OG=827g1AvUYzo(Uri=7X`+RnkrU6y6{Z3hd&$yS!-u9zlL(^JvT(McuH z%T6y)OW!8Y#YW1OMPBZTjK4G%z|GFb3gPeO>h3M=FU#^5t~B=lpJYK6#9t&nF0w2} zn%W3O4=+1}sDP*dlK+;!ldmv~+!ch3m#w|Du9EWKA+UF{ERH@tp3;JXetv!eej)-M zUJimnQc_ZaNMS)?VSX$HzjuJUkCi{ayEp3}h`(Vd*?HS|IeGdxdAK9~U|LywJn)fa zVZr7j{=uJ{r>5pV;oZIet_7?<1pTc%1%(8Vf^Ket|60P^=aw%Pe_jGJn*uyyX9-=?!)@85Vkh|EbsZi%k?jDY;6SXT_lyB zg#QKNj+YbGE3I7rm8(Bcwpb_|2@we?AuCCKF$pOwOE%(2ekmKIIKP#+y}g~4m7R@* zz3pF6wl>nr9$s!%SaUkLSvlAVdb&IOmGMX6(hAzDvMj;^$p1>wcD3@c#}>f00Vj7` z4?pk!%F=gov(xjj`lC-FaS^1Lq?E9@goKckgqYBO5gFQfd1HO?52_GSKv?vzoIi$< z#)^Ygt<@h-#RB}5j+I7Q(aX-t$HPnC!^2gUd?%%bAtjim_qx0SEm-?V@&_qQw?M=N&+J8TR8yG#8e-Rb{e zF03ML?M1Q178Vm1;m6w84r?x3VSX_y5j$afB+}YO{BPp@6W!ax-p9|%%TB=oD=AhQ ztPA}m4T9_M%H;k_?0?exIokcv6jE4-A1TF;6w()xkVXnii-@oY{_l$?Bw{Tkj1;xv z7qXWU;}=CrSYaJn%$DEU-bzA5TwL7B&Q9dt+5I08Ph46|{Qrh{NNaI>DG4hPemhBV zdwx+{Nhy9SNpVSjdwVG>J5i*VxRr?Y|EPGv`ba5hVF_tbiGPSEBlzcu{10E15&Zvo z^WO;m+ zTg3nIuK%d(zomiy7V&?)>wipL*cY<@n5^2lW5=(4*s16>(+2D-2V9`7p?~W)_>GDL z@%T`n&jl4uw#0xKeHg$27)(QCIC=~X0LtjJ2Znz5P=yFa!v#BKgE=a}9C4BX`Rw}Y zLh-X)ZC&j#0AlbxPnDskTTrO9Kgu7R=0a7FFzs@{<(YMA4+FNq(b*BGsMNRu)J+r$ zAHzmML`qIt0|hXahRN}vwAA?1B^Xi|xp%NtRaeyykdm%2?OcFB!48w5jxgA%G(cV% z0{B~}G_Z1f?b8MrFhT4kBTv!LF*KK{x#=MQr$$g8U!NLRE-npNp)FPr#87X)Bo`5a z1!0L_czOX&eX!{W*cF&cGAM)s%OMc|I1;9&e>w&|MM52wXg0sC*E}mOuPiQpR&`nq zGm?QBH2|nH6l#4r^lGm- z{+@p>sJ0Bo2WVfZ!9c<678L)q2_FSBLY`(J zmt(jbZ7nTrE&U(*V|=)f`mkhE#L&=?zXqbFxTd_a#z>N8d11LQx3CF|1LHcq0&A$O zX>Gy4h)%IDGyWgHD$OH%u_}OAtKU`vF?ftB*c-T~im^8UgcN^%5Rj8ggH0sxQPsRf z@EMLnOfSe8w0jN!0;nn}==*=%Hj_=7m#!xnr*YP*kVb=*8d4wq5{YGH%jY-qq;H`c zB=L%FVw|4!CcL;dZ}ht5;{A(;8sEodJnS-|&hK+8Ul@N}W|MA* z&K}(K6XybbhiH1mW{?wKB>QCqRNGGmj*jYM3eMxU!M%W>YzFN*#OUo_OdI|d7DdO>4x=acKs@qB1I zjiDbKtQe+fAU1|h1_?rYRm|YU^P>AIZ@MLvCVYI~a8(qZ?pCX78Hf*#{3_WWy@~pf ztYbA~Nb3}~Q6UY?5|3?`AL09TeE9Z#b*Rv}F|+cmg}8-VdkqR`E+5{+h4_CYjZrOOcim3Ke5QA`vw49b&DH%sYH4n&ufK>Sa!&KbYK zS1LFHsw2RjIp^^JsedJNall?5%GAK)_|30$FIg^evVnel@`wctg+B*a@aIC`zVV$* zrqbdX2+w!zDveZ}j$yxOH3oom!!aT7G(-Y4?d-o1w57h{bG=SSO*xhkXR1C5O_SfM zvgU5|+hK63i@c`W&+a`A`Zco`q1qzNyW!9!(q*zS8sqnsHNBKK?tCg(2s-aSS$raA z+7|kn0hQl&HM%4ENm54bd54K0dRNnznreu~6PIjxsjzoKHoFWLHH_VRlV&&8-6>B{ zccAz(6vHGhb@odLb5qN+RJyahY{oxfc}phrO#M37)${SPq|EgnyQ6ZK$H7(56+yL* z6WWoeOfq9(qP=UEx2c3L2lMgJ@!(C>*OSS^&Nb`bW4QN72!7HYJpY2Xw2y?|{`vXC z3xZamDH;;U<&W?c|HCMBrD}o=DZ?gi6Ev9g_et9)jkV#LKOHI_+Cdd5$SEIXU5l+=S<1Xg`Hk!S*N(fl4;z;6tn%z^KP#pFVTFLM z!U3IVF_aICKE)&YZe~-J@$q__&_c@F#(QaVae8(_s}{P{zqEKR7cMVUJN8{FsdaBW z3FIShp{l%G7q-v}dEb8n+McJeDG;d3O9b*|4RE@Qp(B0LPv=|f^W91~pmm}OUnfUe z5?(6oNXX?^6!V*;lD?jJDAvs4Y{CY8B_qGHy?_c1IUY3kR>8$nVSDaqf&`9X}XKTP_(gi^8gC zbO+5!m?Qu`H!cpv!S8wYa$*Zh986Dl)8H7V=5E3^x8{vBQO)@e-`B3RaPPDAdkok9HS|%|V`$#u7@7@8R?UQ+qu5>}Fl75lO|Q>>?ja z-}qFEe!j!zCSg6j$o_#;_xc_zCP~!JFzWW&G@oZZyn_^X4j+|AVwd3adA&QEH>CB#5 zO#_D`vEQ`ko2bmU7y;Xc!;Qel88081n5Fi|>}mSKQK8fJjjH)@@U+knr(xtQzc1zd zI*X_CwpoxUez8XaHV`9EYK7rPs^^nAQRoj$9XIX6CRw1`ger^;Ibhi!8yDB|^@pd5 z%SRppujJEyI$sYpl;6TVd2jkFfDmm25RBReqL2LCWa}K)GVUyhAM$fcDhhwfcf3in zk2de^&oJS1=BlCRWOwvQxHH8%jPp>ed@_=c`=@I)6#aPqy2P+m2hMUk^g>E(`$6F8 zQGL+fiGj{lNFgo2a}s>UR`S+Ytwxb`^Fd|42_P4a?H|eGsilf>_G!XJb10Q#4EW>I z9HBiD0EQsG3fW74`m0;)guwh9_f@k*=n$%{T?)#;9RVESVEmqql0?21o-Tvcd(M>& zV-zH&5jE3As`Addu!xCr!IKm6usU}mZp7qTm0JHTghDga(g@cx6!tJ*>DN(xx@SlN zy#%)Kq9qlWR>w6lx}M2 zjz6eXC~n#eRIElb>UZvN8l*=7z9o7y8Qb*3md_h{ssz6<`V#G0$|2lmRY`U z3-H$E#n;uJ03utL({x-!BiP~uhq&N^l+5Ug)YY@=^gNb?pdJSmg_89b#a|vTRmiRP zeO`LYZKjU|Tz~DQ;i#~XOCZ?ylT>UtF7;)@@iP^)MDk75pTIwmL&IMvM!@7HfYI;X z2iSCDFL~b-Wi4vz-6C`sJWwnMKN<*~q z6|lj;-Ram6w(PTT@Y)Y(6&0CuwjVg<`V*+=o$f-Gzut*2nwb#;uR?!)H9vdu;Ikl( zwk4fw+(YuPHYRe%lU!vy?nk|v5zNCbACO}zQoPIwbUB+UjXHxksMjibV=g*6{zhJ$ zSCM70T{m!s>(En8}}h&J%VF(ZJ#7{roK(7_lQ1Q4^X^-mfVj9%20? zEUg&Y%K)+5^vF}6aIKc6Luw&aO$t(Eq7&0FVRABNFF1%XmWIys;8GvFrv+6ogH5jS z08y_e6%(xt^;XfP4_skRO*fAwo8hDG5d;)1)f|d3F1MEADQb#MYDA`R&>Q4vev4)K z4z_7N$Q~T6LG60B5x``HQx>Gm21c|~$EvsqS!%0qNm^Y#vIQB8+F-^S%)7? z)WKg7s8rvwO4+X>K}taUO&qtbpf$dV2>NosRwtid5}$dzMd$chA5lH^?&FnZfQ3Je zsC=jY;)yXP*G@KutPYVNTo*V@uAO6%jQ#{g?`|mYLNr|{#TR+iB{-CPiMz8P=$tru zV~B>MdFHH`as(8!S!h~TciWTHCf~g>gB^0=M>{qgho;Nd- z-mPOBacR}F(k@@D^NeC-op|ADpfQ!I^z{bF`a%qG4w8qUCM=cr`qot4DR>#VTg*Q` z)|S2(&e$ZQoFhnxjqDaGo$6%soB;YN@r$84J4v<=p5d$?H$b|>B!MM?OcEQV_zDzk zf!8*xMt5=^E6TNUXbI;M5KmWWcJtmOzWu68mMBNmZPDvaip`>AJUuRTjx+NJ#g4a9 zlrFU#%d2G5D_#$I@!%kX3LS!j>L3KQ`_V%BQ3LN`AHYFAU`kP zL|VwF6MgjvPYU{51y>~-$dOZuafWdfbT9Fq&q=oi*JYqX48!-&!Pr=j#elm2@$-f8 zJ_!B}`KX;|vCu$-HX)RjTUqihGaNNuqWXULi`dEROG!uiUpK1a?s zV`+4@3>8>>;yr;^yEyyV(PEaU_k%Sf0l6sKJ_#MsqiZ$=@e=A)s-wuw` z%k+u6I&$~hEMFlrsRgdKko=k|IJKf3B2SLXYq5iC9>{lp;76nub*@;Rl)nw&4#R7- zH>Z1}V+{w~0}k8@9l4Mg#hhBQYf(oc1czmvZca=?d4(&%UTR$=!E|$lYPmE|*zBsL z@3|D=Z=Y_d+MP$XpI$y7ibwg)4IN*p5fZ=!zd|%Lb!$CvD&>l zdYZl!A**3jUMOb7sqEZM6Hm^*bU1(Gz4VnLTmg2J><9X-Oc1$V!Mi5z``tfhbx|WR zy~c2%b5=tfs0$IH)ifY_rNX8+Cy*xwFwm=^;psIzz}7_y+ZKP#4r<8ql{0n*<&1bb zb!+p&iLd!cuxrSy2!&A=YtD**ppD}=cA99pna8iGZp4^N*F9#ap;S{}mLgHN*t)ev zxFT`bT;9eDF!SuSx_59fyGivVG0N!*(>9+JH99D+Z%n2PG=3=|+;iyffwW2DXI5E~ zWallv6}q~5g~Nsj4=5(L7D@JRzt%mb`M65jGZa&mDpUs|?fkPK%QX8>3?rvO@(m3} z+iy>jty}ctuiq$RO}>}+_!@7^{pS=C`}K)yR-dTKaE1+AU2(WS45-TS&!nod;!S|g z4?;Y{P|OlU+fDHCFLR)=Nl9B#esc>wqD$9au$5LYX6B&%$%wM?1wLU$)~uXU*b=*X zk?aRzmwXD&-Z{TPa#s2nqOWGu0rx z!Z3$D(T4PW-H|sHU%fPxFR8_DF+JGAx%dA$zO>NI-8w3ImzIc{d(tHzRY= zv+@lt;46^xC0r7E_S_I(E3|E_gW^@R55t`Yyf;k;6VbbZpLBRNAEya-Fv_r7q|ZG03j=;? z<yW>S37Q2;H~8-z7HYKL0xg0ZV45NZO4jG^xmiS!)@5i_xhCos@WhmL1ed@w%D zR@=baqY8e%pa8a=@?@E_<2~dg@eP62WN1;%N+vawb7!o3iQxlWAOhJ#IS#!GVYJ)3 z6BrwdD-yf&;}yhG31=)8al@Fdp{BEwy!)^$lCSzhLwN9Qb8>cO)oe3km4*IqK#82t z{#`5~@KEsgHmj6?lsEEqKA>nH5iJZ2>0cWicjox8PaoO}M~xAP0QNf(qU)^m)azLu zoqV+nR(FI+X~8glO7){fB%6roi$oP}(ZpM%ed{M-*D|e{X==l+{bDxDCym`z#jpa{ zWrQko2J30k_VMV%?sX5`nhq$&XVDS&xtYnK0bV8boU7yQZlUW{r^+ z1RG>2kj^!d?cImM!f|#Z5>*`~b7oBncB96H6xDIYVaU@mbXLiLP*UU>L{1sNACBj* zp?QvKqf@%82E7#F?{mJ+PZX8|<~Wst->2tC-<7cQk;4iL2-=vD;m0uV#ANYVHjB;- z8O)OkY`~_Zn2|ecBJT4JJd{ngd7q4cC4M|i_{};^pn|82QVkQSUx`U3I2<7Lfr-Zca9bN5v5|o!+-+_b9ycklWTfp5Us}}0!X3QoQQ6BofpT}tVQy>;Y#`Cc) zw!*1)iB&fz;(Sa(3w|ZDN}xAGI)eh2LAmntXN0L4WcdcDybb*AGIGIQQq3aP+I@6h za|3h+)G+JYjpAAw1w^b9jwz)F6%)7=`DzZmv}J}|IcNJI+K1-N;-F=2C{4)2gTc0v zZ8|N}BJWduR=xeYk}>#>)MtaIKIq~c{aqZuhOjB6UKGhPYyC8DlTYs}ZEHmOHTB83 zX3se*iv2}4v)-WleP=&e2@K0>2l} zph=T0^)L)4g*h7=oSh`>bm{TfE*`qA)9f>KQ}97Z(`Suz3U|HLKIt_vtrAVld_RU9 zH%X((`WAs3jbvs83k7`Tu#v`%*giJ}hwU}3Xa@;q*NDmj7kLeePC>heYs%%)g`HqS zKghqEk|E4`vZs>5Qj~RSG3)ZEO23QjS#ODM+$VeSY+|aighXQk8VM*2ec6gQXu$M8 zf|3HY{T32ILXJUL^VXFpp@Q&L%y;SWRw>CVzni@X9b!V*RKMLE+Ock3lJ5OjmA*`e z{w531B>l zTJm=m3Z4iPx37`zDeH!(zhn~upLTO;Q3i#?wHkf*OKcwXYr8xlLaMtmjHD84gJl%6 z@i@~o^es2FLdx#IM}?hUG0+b7Oc^c1Wixsh^bU=pjqN?qA}WYC>Jdsd;uNV{+3vN2 z&8e5YF+Z8hk|P^jF`~;z9kAoacV1-XS|ile)HQ==L&jYHa=qX)5jnA7?0#abA1I6+ za2}n`E*=twr-j85=#?pwXly%LeB=ln7hqK3`mpk{g3iWJrcSca_}(54t*N<_akx5= zMC!LiV^0ob^Y(pXIV!13LRx0+D|ED=?!*$*$I4^iJq;u#eFe0loOcU^YX?*1C$0`b zj;V+tB3B{!2zzyHS>6=gu~C2`P*P1Xr)GZOBl5Mxr4ujvliCCxJRbMDxO19s+0(n- z2mUDtN#`zgpp=|n3Z(|30iweJ)-8f%3ih{!eiL*&dAZ2LIGrK{rK$3%XKLJN+B+o& z<><2ekt2j)l{CcU!&0-2lLV{hE5lBG)nX%+S1!I3wa&Oe95sv97?mxH&M zK>hZecT-T~?u6~!fx7~4chrJ*)-xA4IA0C;GtKV5##Gk!OW4UGtvMSWE_3&J=r|-% ze73rwrwUh#b1oeRBp7drW=uC=N5ilVj%SbeF@FSuO`m|+~y(WXZ&S zLc-ny$v+-03mp;bKG)?`M5ll1;q6nIdwr_`4^yAfLj`D_zqeZ0t{mFQwXlU#ScE8* z${;s&hfV8>^D4v6!?y3&U623F*7>Ylz}6)1)jQgC^*sdST$k&VBn>F@dKdR7$?zFjvst zPQkywM~XV-Eh-~7c_OT4KJ;)IcYVW&_y0EE(Qae%#bimTWW4-VxGOtzjYJuFadJ4OcL`9qjLDtM*1GzEtA`ea5{VkFmFXjFnkFUA_C4|IH@x0v=?zM< zudx)Za`6cjo7!~I-Uz!{RSPy)0lR;yu zMg1Up^z0Qh9^##vCR=`$w*~nCtkfRpj*$1dl*#nf6Y8^~(?-bAQmhI)k4CED!fRFb z0>*X_Bndh>z0fS;BmVM(_v9iJ>*kC)wps!X1K5ui5&)Db;(2yE_z!dC`F0wW!)1nwwG9tBbqZx zXr*?Y?b9`{SWSSoj3N!3C(dmvScArXPIbeVtrD-QBA+Z;fBLMQc#1UPUGq22RDCCdl5zIvSyJJT zE;T*xB^?O~Cm6xbFphxi>SKgMMeVB$Az>ZI_RC8{;n##9(Uu_6wvdF0T-K8&dP~uD zUx|YoA2cq-g3XUHQQ&L6c4odG?1)K`<0c)g1xpMRjC+xJK^0yJy*>UJ&t;`BH+ z6vp2F;YdOQqMzT9`I8|J>YK{P=7c@}Pbcb(C~{O+9>MJ~6Vh+Q=^K1xXDiFE;fta^ zKozBG7Nj)xkWWQudBX^h_}=RLO#YoOfo)%f)%w$EekX&Y^`2OCFIzU9)9-wUBVBjg zm3y&i?Pl>{XMdCeUp|Bna=IU&OR9gw4;&Z7#vnwfWmTo7naa;wf=m0$DbP(<9K^uY z+uH1}M^x*-v_Jr^SoH%%1cd;t6^>o3EiXa+Z4&|HNF$_tl9AmXj*&OSo6Y|%&fPZi z=nARs_C`iFA-js169n;~I+>Psa)enjdBfQeW@&0m`4xO~SMhtE!d|7%Y$;9D&u*WN zuykhKRe8m(eAemdszMRpFUi4Idjpi;kY4`LlTH=B@aC>MEhXiibp0mZT`lv6r06_m zcK^bSRGl@uZ)mSk?^6^_)Q6vadEFOqph8eQ!H14deJ(+C%S=g9 zU6H%P(S;6TbQ?wuvicxRjX3SWPY0e+$Q~wDd~KX&1c|kSW)lpZR|~%`5W*lvHLZQY zO26t^58v9uRbXIID0>GSFhrB2LQSPfm!(CXcG;c;&0tExT(+ocH&fe-W zKsA$V8C$#TKTh@ydI7-{L*MwuU*zXKrvS`RfKMKQwy|~{aLZ}Xy}?*GnV)&wtKt=T ze@AMI3uL<1GA;k8BRja3bTftG2g|K+lR4>o&07b&pwT`I`)+h9tdi*4R#|28ogAsW z{xGby%mu}xF&m13Gr%oO<3d3#j*egFCsxD~SQ!AMW2OD+5 z;}CfPp0`X8xDj}$yR~Xsdi4`dZ}@!?bO*eJxRrhxXfI~S7=&TJO0BrhY+a_}YFlTR zqQR8#o#f|5>-bdLKI2!?r6r&8h0j2O`4ov@%rsmsUnlrZN;9C!jCj`O|sPi zoAen8C`ZPq!QpF>g;l9l6M@n^89Qe~izKpT>rIGvqC!QfAMzX+ZV^j{fBfz*U9Tb` z`O@7!*UDoy-?gmHE4r$Ry)cjkbn-z6Tp1dM%I=l1JM;f6d@=hrPbBo1-O`IlV zhv~Chq~A-}_>z~B3O4jtMFI5S5(F$JcitP)&ln76cQcF~Z#gt1X8I2Iwl#t!hq1M( zlC1=-QP=E{0IEpNA~@s2xZi5lCW(OU?3G4jof4IyKV zcVd2SjQ4)mCD%S9YYfhL?PZo>EbE80crrrZ)UOS+rzoCO3P6ziALu<)Ft%-M{E37q z7v-uZe4n23F*ZiK-mL3kzt$0~04ekWqW3p*i@pV?St_d`J>ERPz{_CI4DjY|sJ0kr zwI+5g1Td$3vX5oI3yk2)>)+Jc21zAn-p#&PR34}l5v4wHyDpfRWjcyui>Ff8>Ps|h z+GG$L&_@p}S%RtA%hx*TMP3JANQ()qq>MRN;hIwiE!EYse&X`0+!?OJ0~0UnIqjd+ z1N|D@uEie0NcP#9ZH^z3xD!8^Ix!)1rV8sxQJqGR>$3rpw{tkd>vQdf^c7qmEf?1f z$%)whw*PorSS)C(M#zw$$ImO~!X4;)$4@4fG)YaWIve;$U%u+otyk5%KjBX+Ik7JH zMnIOTvWTj{oZ$x>H*?~*Eul3cBD%I3-W|Anp~GTHjI6le&??NdhhM2s>TOJJ3J`d<)@g z02N~~2W(`DU9^JLlp+GE4`3n2{n4u%W?uJ0Rbku}+@(E@mJP3d}mFN`#vC3<@v z=%nVV)?+H0a1k%8m(0dY1m;ejm|1UreDYE<+r;S70tYBAJ5X8kt7yS5M#_}5T;!Mu z{Zf3^_ah(XaOLMl{(i(tH;1>!TVlJvP|oBOXsm16EA*CA9)9SvS`4(c0Os1mk30L6 zzzTpwq<0*b^Nmb(4Xpz8R90e$&yQHP&b3msjF^(u5#I!@7DrWx^9%> z-MCE(Kz9K~;#VA&L+xmLVQr#aIP0f6Z*Vh-jA2{*6Wd|bb7Tm{w`cvDLB8GOfM)3` zyWTl-anYy0{wnkrSk6Eg~Q*F`)`ehC6(*)oJ9T$w<(*jfhG|$p<06aP*3eMgw1rZPGgVy}F)XX*RG-C#tCq z90UjFYY980{QD`7tfraX^GW>rr2#}q(0tg%P>AHs8wUMEvGFX?n0r8*3@ix|UT%?= zN{_CI@%rs#1W zEjk_o8?nSxFun>#jNo#>n`}o&!=e7>8ws67u9{5QX&7myHRk@f27L*DT1V|a9#aGl zDKlo}MfgwWzh_**?rvSU10ogDwuy2K?M1KE3HAHhDH)FHWXg(F$4rfqn6HvFpmt}k zA4u!i?n@M(Skpm7eGj160ET3+(#C#Obe$J6@7Vc)+*l246&PLLOZj^fn@RpHV;T31tZH@}EVvez%18m3!Q9@gJ2llKd+G7pW{i&I9 zsa@1OWTQCA6qJevy59H2U07Vcs>(5uM3=kSyoR8TXaEK7)&seqlNUD8)pzKFX#i1J zp7VF<*%ODH4;UBbdiM88eZL--$C2#RY=U>TEx$+u@V+qMW+scGmQw;>FaS!CktkHw z8$6K_M-ozjY+um7@J zE;A;&KWwIc7dPieOWuR%Gr=k8-4vE9QsgOW3HTTxYl{>}Vc0WIWy)SW$=46v=Qjfs zz=myQ9WCevSr)jMVZ2nGQ;VNCnLtT?;2MXUFOj2rbfTd1p8V*SZnC8@9Dsx+Db}1g zwv=aU1jy8ZWI+UzBJT@=A*LUmd%{=CFZ`Dyqy}sd?2<-cl^rCVh-DyXEM$&B7$_+0f-+FKE!H|wW!U4?ir<%yV4 zU2Z7xd6F;$V~T@;*d4KXj6o1#I~zpcSoDC^Sqt?W7QuQ{<#$M~OYj8`K+c9#LqN0- zc5}uqN&up*8V&~~HB(tziVZ- z{K%YV>ZX@StAY}Z9GXnz2aiW9R`&LG1^`TTMaF(svOjxNxwJjO%ZTHZO9JiL-VnZG z$fMfi*FSpln`5IWttr0o-MaSRZpzCl_R4&XecRqr6;FOB`RQ!C7?{f7zPRxi4?TZf z{G34uMOku7_R;-{%z(bTLeJ)IKpz!#)(_#06W-&m7~poMWq-@{m`0Dewn2%cyb*h- zcsn-=Q(H{$NCh0R>vqzB<`KtM-{L5vnAW4)g3c6!6g?~?EQ$28 zF#a>2g9U>Kt_ddJrL}gD?lC01O}QLdVwWqlR!MWeF{~KrL`ML=yzmzXRge1FBr}HK zsSGSC%wg572ai|FjbqotVv{sC)HW60@9lra4q9&X5a^u5+g7O}R+L`$f4OUYJ56M* z{PNQ1YQGMd8V`GMrKiY&8{^(R5YxTnyiz0Tkkrb|POI;FE}W_RDCf#HzO*Yu`GRHI zjdkhmV_S#lQ?7!66t*i}g#xy+tVx0FD6;OL;fZ{Woq{vm;{E374L+vX6 zlc%M7yIfv7o-xPg!4pq94c}xCH6X;AyQwO^Zp~1>TPjMJ*`$wjna3U*>g0e0F-CBoXRkgvbGU^3HusE-itfYBkFNR2tsHG8h*pdSS2$g4R30o zoq5Gi-zow6>k-{#S{9!O@{1{!xYj!O)LoULzHNH$oUD7LcHEZp1M;@PVxyd1Bz18`xC8Uim>Y z?|fWq@NP+~&V>QD#Nf}HJG=SIpBasHdcN?PY@{a-=ZitF#i2%;U&$(?KGwM=xmko7 z9N)Fu-=J?A`y9TuysumtgWHge+-}&)@}~l{8Fqs(c*B4LWBBF6@Aru1=E)a$E~J+} z2WA%XjTIde+UT>hAky)6#dYJw5Sl){Y@GE&Xnt z?h1tri?%%WYlTF+S((XFfPm2X%QQZ7dMFK2MHzN5G> z1EkO^^C!!+BsO*T%>(^7=eM?nLsySS=)CYyC~f21OSbFAQow(0eF7{i6o&5DnqB)Y ztatOU`q^*AN27E2hw`Bx^Xy2_oF?Ivf<~7;p@Xxgw&5N3Hf#oFKKH86qYh4f4@ExT z8Q+!jrn8kxt(x2lsr(vwDxvn^t!qcU!L?Vs1Mlhm@Eu4jL-&m7IU4Bkpy;-QZ#rZz ze`VU$9@@Xz@DhLImcP6UP{J@lD#*-W*eKUrL%sfpC#l1xF*(8pHTPPL&->R!^Mdf( zg`4Mw&9$G8zl^>+a-Xqy9h%ziKn#&nDsEun397y@WqMQ+lwx7l#$3QdE$Ef*wX&0O zureMzZQj{db2>Y``}+_T5YYVa*TL70I+eG3L1z)i<}wzOxn%ciG>+uj5+-~u*~gC0 z1_ys8d_DVo{_^9XeP65V!?OpY0(5)UgOl-REANOyb381AS0o^w{#yK|xJ|#>-vw6* zcurZ}6?+BRHp_3bE&mL05BYE?OA+pZnX!-z{xq1b3X&+6LX zv#n=NKaLCyUbXKD!Ht#!-hL5KUMQQF4SrB?&$`inC55?VXW(Ay{fcX^l>EOpzWij? z=2fI_F5ok!OYKISJ%}{tz!x?G?VCG%HID{~!OL4G)282vvfSWJIXmWZi_m4=19Jhs zXZ(Q5=D;3z_fE%M#bvEgSCX)xsDa_pwpLS!pOdo%o!njgQNGw0D?V@Omom_*aPuQf zDY>Z$l|4$2*M#qR860~w?Ri}wlUT*QcRRZUzZdOZ zzW!aY_ZcyL9lQUTzrRhht@Jk1oBC)fNB-SwUw5w#*DEbx5a32S@Ac=rb6ZFl*b)KS0F+GP8bN41uVZ@aDliKi`>Zb=eD6`!_3wqY+;=x{+wJVv z-S^?u^Jg}DRvwAlX7@ey|}a(>B>Hnnsq$$B89>azbrG~qsus1XwR!gU)3F3 zbn-OjN8A2-I!`DjI^(u;f^Km5_1%H*w+Y2u{atU#yKc$@_n#W_PZ!rr>HidHcZw*^ ezt^Y#GuW}dwpKse*#+Fs#o+1c=d#Wzp$P!Vxp?FN literal 0 HcmV?d00001 diff --git a/arm9/gfx/bgFileSel.png b/arm9/gfx/bgFileSel.png new file mode 100644 index 0000000000000000000000000000000000000000..489e12bab02228266558ce2d09f45659ae80dd6a GIT binary patch literal 15706 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 bg`sG$mEfURrLrP#Yyfp-9i>_Y+sOX~CHie$ literal 0 HcmV?d00001 diff --git a/arm9/gfx/bgInstructions.png b/arm9/gfx/bgInstructions.png new file mode 100644 index 0000000000000000000000000000000000000000..43b91161dd11728147e47f5f4af77519d4d49941 GIT binary patch literal 4625 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 literal 0 HcmV?d00001 diff --git a/arm9/gfx/bgOptions.png b/arm9/gfx/bgOptions.png new file mode 100644 index 0000000000000000000000000000000000000000..132093afc07569c4d9256160dbd928f86fd601ed GIT binary patch literal 16240 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 IxM}eJ18wz!4*&oF literal 0 HcmV?d00001 diff --git a/arm9/gfx/bgTop.png b/arm9/gfx/bgTop.png new file mode 100644 index 0000000000000000000000000000000000000000..431192022ecda9e5ba009758681bdf6aca595a6b GIT binary patch literal 25202 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 +#include + +#include + +#include +#include +#include + +#include "ds_tools.h" +#include "bgBottom.h" +#include "bgTop.h" +#include "bgFileSel.h" +#include "bgOptions.h" + +#include "clickNoQuit_wav.h" +#include "clickQuit_wav.h" + +#include "Emulator.h" +#include "InputProducerManager.h" +#include "Rip.h" + +typedef enum _RunState +{ + Stopped, + Paused, + Running, + Quit +} RunState; + +static short key_A_map = 12; +static short key_B_map = 12; +static short key_X_map = 13; +static short key_Y_map = 14; +static short key_L_map = 0; +static short key_R_map = 1; + +#define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); + +int bg0, bg0b, bg1b; +bool bFirstGameLoaded = false; + +ITCM_CODE void dsPrintValue(int x, int y, unsigned int isSelect, char *pchStr) +{ + u16 *pusEcran,*pusMap; + u16 usCharac; + char *pTrTxt=pchStr; + char ch; + + pusEcran=(u16*) (bgGetMapPtr(bg1b))+x+(y<<5); + pusMap=(u16*) (bgGetMapPtr(bg0b)+(2*isSelect+24)*32); + + while((*pTrTxt)!='\0' ) + { + ch = *pTrTxt; + if (ch >= 'a' && ch <= 'z') ch -= 32; // Faster than strcpy/strtoupper + usCharac=0x0000; + if ((ch) == '|') + usCharac=*(pusMap); + else if (((ch)<' ') || ((ch)>'_')) + usCharac=*(pusMap); + else if((ch)<'@') + usCharac=*(pusMap+(ch)-' '); + else + usCharac=*(pusMap+32+(ch)-'@'); + *pusEcran++=usCharac; + pTrTxt++; + } +} + +class AudioMixerDS : public AudioMixer +{ +public: + AudioMixerDS(UINT16* buffer); + void resetProcessor(); + void flushAudio(); + void init(UINT32 sampleRate); + void release(); + + UINT16* outputBuffer; + UINT32 outputBufferSize; + UINT32 outputBufferWritePosition; +}; + +AudioMixerDS::AudioMixerDS(UINT16* buffer) + : outputBuffer(NULL), + outputBufferSize(0), + outputBufferWritePosition(0) +{ + this->outputBuffer = buffer; + this->outputBufferWritePosition = 0; + this->outputBufferSize = 2048; +} + +void AudioMixerDS::resetProcessor() +{ + if (outputBuffer) + { + outputBufferWritePosition = 0; + } + + // clears the emulator side of the audio mixer + AudioMixer::resetProcessor(); +} + + +void AudioMixerDS::init(UINT32 sampleRate) +{ + release(); + + outputBufferWritePosition = 0; + + AudioMixer::init(sampleRate); +} + +void AudioMixerDS::release() +{ + AudioMixer::release(); +} + +void AudioMixerDS::flushAudio() +{ + if (sampleCount < this->sampleSize) { + return; + } + + // Write to DS ARM7 audio buffer? + memcpy(outputBuffer, this->sampleBuffer, this->sampleBufferSize); + + // clear the sample count + AudioMixer::flushAudio(); +} + + + +class VideoBusDS : public VideoBus +{ +public: + VideoBusDS(); + void init(UINT32 width, UINT32 height); + void release(); + void render(); +}; + +VideoBusDS::VideoBusDS() { } + +void VideoBusDS::init(UINT32 width, UINT32 height) +{ + release(); + + // initialize the pixelBuffer + VideoBus::init(width, height); +} + +void VideoBusDS::release() +{ + VideoBus::release(); +} + +int debug1, debug2; +static int frames=0; +ITCM_CODE void VideoBusDS::render() +{ + frames++; + VideoBus::render(); + + if (frames & 1) return; + UINT32 *ds_video=(UINT32*)0x06000000; + UINT32 *source_video = (UINT32*)pixelBuffer; + + for (int j=0; j<192; j++) + { + memcpy(ds_video, source_video, 160); + ds_video += 64; + source_video += 40; + } +} + +RunState runState; +Emulator *currentEmu; +Rip *currentRip; +VideoBus *videoBus; +AudioMixer *audioMixer; + + +BOOL LoadCart(const CHAR* filename) +{ + if (strlen(filename) < 5) + return FALSE; + + //convert .bin and .rom to .rip, since our emulation only knows how to run .rip + const CHAR* extStart = filename + strlen(filename) - 4; + if (strcmpi(extStart, ".int") == 0 || strcmpi(extStart, ".bin") == 0) + { + //load the bin file as a Rip + CHAR* cfgFilename = (CHAR*)"knowncarts.cfg"; + currentRip = Rip::LoadBin(filename, cfgFilename); + if (currentRip == NULL) + { + return FALSE; + } + + CHAR fileSubname[MAX_PATH]; + strcpy(fileSubname, filename); + strcat(fileSubname, ".rip"); + } + else + { + //load the designated Rip + currentRip = Rip::LoadRip(filename); + if (currentRip == NULL) + { + return FALSE; + } + } + + dsShowScreenEmu(); + bFirstGameLoaded = TRUE; + + return TRUE; +} + +BOOL LoadPeripheralRoms(Peripheral* peripheral) +{ + UINT16 count = peripheral->GetROMCount(); + for (UINT16 i = 0; i < count; i++) { + ROM* r = peripheral->GetROM(i); + if (r->isLoaded()) + continue; + + //TODO: get filenames from config file + //TODO: handle file loading errors + CHAR nextFile[MAX_PATH]; + strcpy(nextFile, "."); + strcat(nextFile, "/"); + strcat(nextFile, r->getDefaultFileName()); + if (!r->load(nextFile, r->getDefaultFileOffset())) + { + return FALSE; + } + } + + return TRUE; +} + + +BOOL InitializeEmulator(void) +{ + //release the current emulator + //ReleaseEmulator(); + + //find the currentEmulator required to run this RIP + currentEmu = Emulator::GetEmulatorByID(currentRip->GetTargetSystemID()); + if (currentEmu == NULL) + { + return FALSE; + } + + //load the BIOS files required for this currentEmulator + if (!LoadPeripheralRoms(currentEmu)) + return FALSE; + + //load peripheral roms + INT32 count = currentEmu->GetPeripheralCount(); + for (INT32 i = 0; i < count; i++) { + Peripheral* p = currentEmu->GetPeripheral(i); + PeripheralCompatibility usage = currentRip->GetPeripheralUsage(p->GetShortName()); + if (usage == PERIPH_INCOMPATIBLE || usage == PERIPH_COMPATIBLE) { + currentEmu->UsePeripheral(i, FALSE); + continue; + } + + BOOL loaded = LoadPeripheralRoms(p); + if (loaded) { + //peripheral loaded, might as well use it. + currentEmu->UsePeripheral(i, TRUE); + } + else if (usage == PERIPH_OPTIONAL) { + //didn't load, but the peripheral is optional, so just skip it + currentEmu->UsePeripheral(i, FALSE); + } + else //usage == PERIPH_REQUIRED, but it didn't load + return FALSE; + } + + //hook the audio and video up to the currentEmulator + currentEmu->InitVideo(videoBus,currentEmu->GetVideoWidth(),currentEmu->GetVideoHeight()); + currentEmu->InitAudio(audioMixer,22050); + + //put the RIP in the currentEmulator + currentEmu->SetRip(currentRip); + + //finally, run everything + currentEmu->Reset(); + + return TRUE; +} + +char newFile[256]; +void pollInputs(void) +{ + extern int ds_key_input[16]; // Set to '1' if pressed... 0 if released + extern int ds_disc_input[16]; // Set to '1' if pressed... 0 if released. + unsigned short keys_pressed = keysCurrent(); + + for (int i=0; i<15; i++) ds_key_input[i] = 0; + for (int i=0; i<16; i++) ds_disc_input[i] = 0; + + // Handle 8 directions on keypad... best we can do... + if (keys_pressed & KEY_UP) + { + if (keys_pressed & KEY_RIGHT) ds_disc_input[2] = 1; + else if (keys_pressed & KEY_LEFT) ds_disc_input[14] = 1; + else ds_disc_input[0] = 1; + } + else if (keys_pressed & KEY_DOWN) + { + if (keys_pressed & KEY_RIGHT) ds_disc_input[6] = 1; + else if (keys_pressed & KEY_LEFT) ds_disc_input[10] = 1; + else ds_disc_input[8] = 1; + } + else if (keys_pressed & KEY_RIGHT) + { + ds_disc_input[4] = 1; + } + else if (keys_pressed & KEY_LEFT) + { + ds_disc_input[12] = 1; + } + + if (keys_pressed & KEY_A) ds_key_input[key_A_map] = 1; + if (keys_pressed & KEY_B) ds_key_input[key_B_map] = 1; + if (keys_pressed & KEY_X) ds_key_input[key_X_map] = 1; + if (keys_pressed & KEY_Y) ds_key_input[key_Y_map] = 1; + if (keys_pressed & KEY_L) ds_key_input[key_L_map] = 1; + if (keys_pressed & KEY_R) ds_key_input[key_R_map] = 1; + + if (keys_pressed & KEY_START) + { + if (bFirstGameLoaded) currentEmu->Reset(); + } + if (keys_pressed & KEY_SELECT) + { + fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME); + if (dsWaitForRom(newFile)) + { + LoadCart(newFile); + InitializeEmulator(); + } + fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME); + } + + // Now handle the on-screen Intellivision keypad... + if (keys_pressed & KEY_TOUCH) + { + //char tmp[12]; + touchPosition touch; + touchRead(&touch); + //sprintf(tmp, "%3d %3d", touch.px, touch.py); + //dsPrintValue(0,1,0,tmp); + + if (touch.px > 120 && touch.px < 155 && touch.py > 30 && touch.py < 60) ds_key_input[0] = 1; + if (touch.px > 158 && touch.px < 192 && touch.py > 30 && touch.py < 60) ds_key_input[1] = 1; + if (touch.px > 195 && touch.px < 230 && touch.py > 30 && touch.py < 60) ds_key_input[2] = 1; + + if (touch.px > 120 && touch.px < 155 && touch.py > 65 && touch.py < 95) ds_key_input[3] = 1; + if (touch.px > 158 && touch.px < 192 && touch.py > 65 && touch.py < 95) ds_key_input[4] = 1; + if (touch.px > 195 && touch.px < 230 && touch.py > 65 && touch.py < 95) ds_key_input[5] = 1; + + if (touch.px > 120 && touch.px < 155 && touch.py > 101 && touch.py < 135) ds_key_input[6] = 1; + if (touch.px > 158 && touch.px < 192 && touch.py > 101 && touch.py < 135) ds_key_input[7] = 1; + if (touch.px > 195 && touch.px < 230 && touch.py > 101 && touch.py < 135) ds_key_input[8] = 1; + + if (touch.px > 120 && touch.px < 155 && touch.py > 140 && touch.py < 175) ds_key_input[9] = 1; + if (touch.px > 158 && touch.px < 192 && touch.py > 140 && touch.py < 175) ds_key_input[10] = 1; + if (touch.px > 195 && touch.px < 230 && touch.py > 140 && touch.py < 175) ds_key_input[11] = 1; + + + // RESET + if (touch.px > 23 && touch.px < 82 && touch.py > 34 && touch.py < 53) + { + if (bFirstGameLoaded) currentEmu->Reset(); + } + + // LOAD + if (touch.px > 23 && touch.px < 82 && touch.py > 72 && touch.py < 91) + { + fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME); + if (dsWaitForRom(newFile)) + { + LoadCart(newFile); + InitializeEmulator(); + } + fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME); + } + + // CONFIG + if (touch.px > 23 && touch.px < 82 && touch.py > 111 && touch.py < 130) + { + fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME); + dsChooseOptions(); + fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME); + } + + // QUIT + if (touch.px > 23 && touch.px < 82 && touch.py > 150 && touch.py < 170) + { + fifoSendValue32(FIFO_USER_01,(1<<16) | (0) | SOUND_SET_VOLUME); + if (dsWaitOnQuit()) + { + runState = Quit; + } + fifoSendValue32(FIFO_USER_01,(1<<16) | (127) | SOUND_SET_VOLUME); + } + } + +} + +int full_speed=0; +int emu_frames=1; +void Run() +{ + TIMER1_CR = 0; + TIMER1_DATA = 0; + TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024; + + TIMER0_CR=0; + TIMER0_DATA=0; + TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024; + + runState = Running; + while(runState == Running) + { + if (!full_speed) + { + while(TIMER0_DATA < (546*emu_frames)) + ; + } + + // Have we processed 60 frames... start over... + if (++emu_frames == 60) + { + TIMER0_CR=0; + TIMER0_DATA=0; + TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024; + emu_frames=1; + } + + //poll the input + pollInputs(); + + if (bFirstGameLoaded) + { + //run the emulation + currentEmu->Run(); + + // render the output + currentEmu->Render(); + + //flush the audio + currentEmu->FlushAudio(); + } + + if (TIMER1_DATA >= 32728) // 1000MS (1 sec) + { + char tmp[15]; + TIMER1_CR = 0; + TIMER1_DATA = 0; + TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024; + if (frames > 0) + { + sprintf(tmp, "%03d", frames); + dsPrintValue(0,0,0,tmp); + } + //sprintf(tmp, "%4d %4d", debug1, debug2); + //dsPrintValue(0,1,0,tmp); + frames=0; + } + } +} +void dsShowScreenMain(bool bFull) +{ + // Init BG mode for 16 bits colors + if (bFull) + { + videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE ); + videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE); + vramSetBankA(VRAM_A_MAIN_BG); vramSetBankC(VRAM_C_SUB_BG); + bg0 = bgInit(0, BgType_Text8bpp, BgSize_T_256x256, 31,0); + bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0); + bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 30,0); + bgSetPriority(bg0b,1);bgSetPriority(bg1b,0); + + decompress(bgTopTiles, bgGetGfxPtr(bg0), LZ77Vram); + decompress(bgTopMap, (void*) bgGetMapPtr(bg0), LZ77Vram); + dmaCopy((void *) bgTopPal,(u16*) BG_PALETTE,256*2); + } + + decompress(bgBottomTiles, bgGetGfxPtr(bg0b), LZ77Vram); + decompress(bgBottomMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); + dmaCopy((void *) bgBottomPal,(u16*) BG_PALETTE_SUB,256*2); + unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32); + dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); + + REG_BLDCNT=0; REG_BLDCNT_SUB=0; REG_BLDY=0; REG_BLDY_SUB=0; + + // ShowStatusLine(); + + swiWaitForVBlank(); +} + +#define SOUND_SIZE 734 +UINT16 soundBuf[SOUND_SIZE+8] = {0}; +void dsMainLoop(void) +{ + // ----------------------------------------------------------------------------------------- + // Eventually these will be used to write to the DS screen and produce DS audio output... + // ----------------------------------------------------------------------------------------- + videoBus = new VideoBusDS(); + audioMixer = new AudioMixerDS(soundBuf); + + dsShowScreenMain(true); + InitializeEmulator(); + + Run(); +} + +//--------------------------------------------------------------------------------- +void dsInstallSoundEmuFIFO(void) +{ + FifoMessage msg; + msg.SoundPlay.data = &soundBuf; + msg.SoundPlay.freq = 22050; + msg.SoundPlay.volume = 127; + msg.SoundPlay.pan = 64; + msg.SoundPlay.loop = 1; + msg.SoundPlay.format = ((1)<<4) | SoundFormat_16Bit; + msg.SoundPlay.loopPoint = 0; + msg.SoundPlay.dataSize = SOUND_SIZE >> 2; + msg.type = EMUARM7_PLAY_SND; + fifoSendDatamsg(FIFO_USER_01, sizeof(msg), (u8*)&msg); +} + + +void vblankIntr() +{ +} + +UINT32 gamePalette[32] = +{ + 0x000000, 0x002DFF, 0xFF3D10, 0xC9CFAB, + 0x386B3F, 0x00A756, 0xFAEA50, 0xFFFCFF, + 0xBDACC8, 0x24B8FF, 0xFFB41F, 0x546E00, + 0xFF4E57, 0xA496FF, 0x75CC80, 0xB51A58, + 0x000000, 0x002DFF, 0xFF3D10, 0xC9CFAB, + 0x386B3F, 0x00A756, 0xFAEA50, 0xFFFCFF, + 0xBDACC8, 0x24B8FF, 0xFFB41F, 0x546E00, + 0xFF4E57, 0xA496FF, 0x75CC80, 0xB51A58, +}; + + +void dsInitPalette(void) +{ + // Init DS Specific palette + for(int i = 0; i < 256; i++) + { + unsigned char r, g, b; + + r = (unsigned char) ((gamePalette[i%32] & 0x00ff0000) >> 19); + g = (unsigned char) ((gamePalette[i%32] & 0x0000ff00) >> 11); + b = (unsigned char) ((gamePalette[i%32] & 0x000000ff) >> 3); + + BG_PALETTE[i]=RGB15(r,g,b); + } +} + +void dsInitScreenMain(void) +{ + // Init vbl and hbl func + SetYtrigger(190); //trigger 2 lines before vsync + irqSet(IRQ_VBLANK, vblankIntr); + irqEnable(IRQ_VBLANK); + vramSetBankD(VRAM_D_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06860000 - + vramSetBankE(VRAM_E_LCD ); // Not using this for video but 64K of faster RAM always useful! Mapped at 0x06880000 - + vramSetBankF(VRAM_F_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06890000 - + vramSetBankG(VRAM_G_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06894000 - + vramSetBankH(VRAM_H_LCD ); // Not using this for video but 32K of faster RAM always useful! Mapped at 0x06898000 - + vramSetBankI(VRAM_I_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x068A0000 - + + WAITVBL; +} + +void dsShowScreenEmu(void) +{ + videoSetMode(MODE_5_2D); + vramSetBankA(VRAM_A_MAIN_BG_0x06000000); // The main emulated (top screen) display. + vramSetBankB(VRAM_B_MAIN_BG_0x06060000); // This is where we will put our frame buffers to aid DMA Copy routines... + + bg0 = bgInit(3, BgType_Bmp8, BgSize_B8_256x256, 0,0); + memset((void*)0x06000000, 0x00, 128*1024); + + int stretch_x = ((160 / 256) << 8) | (160 % 256); + REG_BG3PA = stretch_x; + REG_BG3PB = 0; REG_BG3PC = 0; + REG_BG3PD = ((100 / 100) << 8) | (100 % 100) ; + REG_BG3X = 0; + REG_BG3Y = 0; + + dsInitPalette(); + swiWaitForVBlank(); +} + +ITCM_CODE void dsInitTimer(void) +{ + TIMER0_DATA=0; + TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024; +} + +void dsFreeEmu(void) +{ + // Stop timer of sound + TIMER2_CR=0; irqDisable(IRQ_TIMER2); +} + +//---------------------------------------------------------------------------------- +// Load Rom +//---------------------------------------------------------------------------------- +FICA2600 vcsromlist[512]; +unsigned short int countvcs=0, ucFicAct=0; +char szName[256]; +char szName2[256]; + + +// Find files (.int) available +int a26Filescmp (const void *c1, const void *c2) +{ + FICA2600 *p1 = (FICA2600 *) c1; + FICA2600 *p2 = (FICA2600 *) c2; + + if (p1->filename[0] == '.' && p2->filename[0] != '.') + return -1; + if (p2->filename[0] == '.' && p1->filename[0] != '.') + return 1; + if (p1->directory && !(p2->directory)) + return -1; + if (p2->directory && !(p1->directory)) + return 1; + return strcasecmp (p1->filename, p2->filename); +} + +static char filenametmp[255]; +void vcsFindFiles(void) +{ + DIR *pdir; + struct dirent *pent; + + countvcs = 0; + + pdir = opendir("."); + + if (pdir) { + + while (((pent=readdir(pdir))!=NULL)) + { + strcpy(filenametmp,pent->d_name); + if (pent->d_type == DT_DIR) + { + if (!( (filenametmp[0] == '.') && (strlen(filenametmp) == 1))) { + vcsromlist[countvcs].directory = true; + strcpy(vcsromlist[countvcs].filename,filenametmp); + countvcs++; + } + } + else { + if (strlen(filenametmp)>4) { + if ( (strcasecmp(strrchr(filenametmp, '.'), ".int") == 0) ) { + vcsromlist[countvcs].directory = false; + strcpy(vcsromlist[countvcs].filename,filenametmp); + countvcs++; + } + } + } + } + closedir(pdir); + } + if (countvcs) + qsort (vcsromlist, countvcs, sizeof (FICA2600), a26Filescmp); +} + +void dsDisplayFiles(unsigned int NoDebGame,u32 ucSel) +{ + unsigned int ucBcl,ucGame; + + // Display all games if possible + unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32); + dmaFillWords(dmaVal | (dmaVal<<16),(void*) (bgGetMapPtr(bg1b)),32*24*2); + sprintf(szName,"%04d/%04d GAMES",(int)(1+ucSel+NoDebGame),countvcs); + dsPrintValue(16-strlen(szName)/2,2,0,szName); + dsPrintValue(31,5,0,(char *) (NoDebGame>0 ? "<" : " ")); + dsPrintValue(31,22,0,(char *) (NoDebGame+14" : " ")); + sprintf(szName, "A=CHOOSE B=BACK "); + dsPrintValue(16-strlen(szName)/2,23,0,szName); + for (ucBcl=0;ucBcl<17; ucBcl++) { + ucGame= ucBcl+NoDebGame; + if (ucGame < countvcs) + { + strcpy(szName,vcsromlist[ucGame].filename); + szName[29]='\0'; + if (vcsromlist[ucGame].directory) + { + szName[27]='\0'; + sprintf(szName2,"[%s]",szName); + dsPrintValue(0,5+ucBcl,(ucSel == ucBcl ? 1 : 0),szName2); + } + else + { + dsPrintValue(1,5+ucBcl,(ucSel == ucBcl ? 1 : 0),szName); + } + } + } +} + + + +unsigned int dsWaitForRom(char *chosen_filename) +{ + bool bDone=false, bRet=false; + u32 ucHaut=0x00, ucBas=0x00,ucSHaut=0x00, ucSBas=0x00,romSelected= 0, firstRomDisplay=0,nbRomPerPage, uNbRSPage; + u32 uLenFic=0, ucFlip=0, ucFlop=0; + + strcpy(chosen_filename, "tmpz"); + vcsFindFiles(); // Initial get of files... + + decompress(bgFileSelTiles, bgGetGfxPtr(bg0b), LZ77Vram); + decompress(bgFileSelMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); + dmaCopy((void *) bgFileSelPal,(u16*) BG_PALETTE_SUB,256*2); + unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32); + dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); + + nbRomPerPage = (countvcs>=17 ? 17 : countvcs); + uNbRSPage = (countvcs>=5 ? 5 : countvcs); + + if (ucFicAct>countvcs-nbRomPerPage) + { + firstRomDisplay=countvcs-nbRomPerPage; + romSelected=ucFicAct-countvcs+nbRomPerPage; + } + else + { + firstRomDisplay=ucFicAct; + romSelected=0; + } + dsDisplayFiles(firstRomDisplay,romSelected); + while (!bDone) + { + if (keysCurrent() & KEY_UP) + { + if (!ucHaut) + { + ucFicAct = (ucFicAct>0 ? ucFicAct-1 : countvcs-1); + if (romSelected>uNbRSPage) { romSelected -= 1; } + else { + if (firstRomDisplay>0) { firstRomDisplay -= 1; } + else { + if (romSelected>0) { romSelected -= 1; } + else { + firstRomDisplay=countvcs-nbRomPerPage; + romSelected=nbRomPerPage-1; + } + } + } + ucHaut=0x01; + dsDisplayFiles(firstRomDisplay,romSelected); + } + else { + + ucHaut++; + if (ucHaut>10) ucHaut=0; + } + uLenFic=0; ucFlip=0; ucFlop=0; + } + else + { + ucHaut = 0; + } + if (keysCurrent() & KEY_DOWN) + { + if (!ucBas) { + ucFicAct = (ucFicAct< countvcs-1 ? ucFicAct+1 : 0); + if (romSelected10) ucBas=0; + } + uLenFic=0; ucFlip=0; ucFlop=0; + } + else { + ucBas = 0; + } + if ((keysCurrent() & KEY_R) || (keysCurrent() & KEY_RIGHT)) + { + if (!ucSBas) + { + ucFicAct = (ucFicAct< countvcs-nbRomPerPage ? ucFicAct+nbRomPerPage : countvcs-nbRomPerPage); + if (firstRomDisplay10) ucSBas=0; + } + uLenFic=0; ucFlip=0; ucFlop=0; + } + else { + ucSBas = 0; + } + if ((keysCurrent() & KEY_L) || (keysCurrent() & KEY_LEFT)) + { + if (!ucSHaut) + { + ucFicAct = (ucFicAct> nbRomPerPage ? ucFicAct-nbRomPerPage : 0); + if (firstRomDisplay>nbRomPerPage) { firstRomDisplay -= nbRomPerPage; } + else { firstRomDisplay = 0; } + if (ucFicAct == 0) romSelected = 0; + if (romSelected > ucFicAct) romSelected = ucFicAct; + ucSHaut=0x01; + dsDisplayFiles(firstRomDisplay,romSelected); + } + else + { + ucSHaut++; + if (ucSHaut>10) ucSHaut=0; + } + uLenFic=0; ucFlip=0; ucFlop=0; + } + else { + ucSHaut = 0; + } + + if ( keysCurrent() & KEY_B ) + { + bDone=true; + while (keysCurrent() & KEY_B); + } + + if (keysCurrent() & KEY_A || keysCurrent() & KEY_Y) + { + if (!vcsromlist[ucFicAct].directory) + { + bRet=true; + bDone=true; + strcpy(chosen_filename, vcsromlist[ucFicAct].filename); + if (keysCurrent() & KEY_Y) full_speed=1; else full_speed=0; + } + else + { + chdir(vcsromlist[ucFicAct].filename); + vcsFindFiles(); + ucFicAct = 0; + nbRomPerPage = (countvcs>=16 ? 16 : countvcs); + uNbRSPage = (countvcs>=5 ? 5 : countvcs); + if (ucFicAct>countvcs-nbRomPerPage) { + firstRomDisplay=countvcs-nbRomPerPage; + romSelected=ucFicAct-countvcs+nbRomPerPage; + } + else { + firstRomDisplay=ucFicAct; + romSelected=0; + } + dsDisplayFiles(firstRomDisplay,romSelected); + while (keysCurrent() & KEY_A); + } + } + + // If the filename is too long... scroll it. + if (strlen(vcsromlist[ucFicAct].filename) > 29) + { + ucFlip++; + if (ucFlip >= 15) + { + ucFlip = 0; + uLenFic++; + if ((uLenFic+29)>strlen(vcsromlist[ucFicAct].filename)) + { + ucFlop++; + if (ucFlop >= 15) + { + uLenFic=0; + ucFlop = 0; + } + else + uLenFic--; + } + strncpy(szName,vcsromlist[ucFicAct].filename+uLenFic,29); + szName[29] = '\0'; + dsPrintValue(1,5+romSelected,1,szName); + } + } + + swiWaitForVBlank(); + } + + dsShowScreenMain(false); + + return bRet; +} + + +bool dsWaitOnQuit(void) +{ + bool bRet=false, bDone=false; + unsigned int keys_pressed; + unsigned int posdeb=0; + char szName[32]; + + decompress(bgFileSelTiles, bgGetGfxPtr(bg0b), LZ77Vram); + decompress(bgFileSelMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); + dmaCopy((void *) bgFileSelPal,(u16*) BG_PALETTE_SUB,256*2); + unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32); + dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); + + strcpy(szName,"Quit NINTV-DS?"); + dsPrintValue(16-strlen(szName)/2,2,0,szName); + sprintf(szName,"%s","A TO CONFIRM, B TO GO BACK"); + dsPrintValue(16-strlen(szName)/2,23,0,szName); + + while(!bDone) + { + strcpy(szName," YES "); + dsPrintValue(5,10+0,(posdeb == 0 ? 1 : 0),szName); + strcpy(szName," NO "); + dsPrintValue(5,14+1,(posdeb == 2 ? 1 : 0),szName); + swiWaitForVBlank(); + + // Check pad + keys_pressed=keysCurrent(); + if (keys_pressed & KEY_UP) { + if (posdeb) posdeb-=2; + } + if (keys_pressed & KEY_DOWN) { + if (posdeb<1) posdeb+=2; + } + if (keys_pressed & KEY_A) { + bRet = (posdeb ? false : true); + bDone = true; + } + if (keys_pressed & KEY_B) { + bDone = true; + } + } + + dsShowScreenMain(false); + + return bRet; +} + +// ----------------------------------------------------------------------------- +// Options are handled here... we have a number of things the user can tweak +// and these options are applied immediately. The user can also save off +// their option choices for the currently running game into the XEGS.DAT +// configuration database. When games are loaded back up, XEGS.DAT is read +// to see if we have a match and the user settings can be restored for the +// game. +// ----------------------------------------------------------------------------- +struct options_t +{ + const char *label; + const char *option[16]; + short *option_val; + short option_max; +}; + +const struct options_t Option_Table[] = +{ + {"A BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_A_map, 15}, + {"B BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_B_map, 15}, + {"X BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_X_map, 15}, + {"Y BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_Y_map, 15}, + {"L BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_L_map, 15}, + {"R BUTTON", {"KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT"}, &key_R_map, 15}, + {NULL, {"", ""}, NULL, 2}, +}; + +// ----------------------------------------------------------------------------- +// Allows the user to move the cursor up and down through the various table +// enties above to select options for the game they wish to play. +// ----------------------------------------------------------------------------- +void dsChooseOptions(void) +{ + int optionHighlighted; + int idx; + bool bDone=false; + int keys_pressed; + int last_keys_pressed = 999; + char strBuf[64]; + + // Show the Options background... + decompress(bgOptionsTiles, bgGetGfxPtr(bg0b), LZ77Vram); + decompress(bgOptionsMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); + dmaCopy((void *) bgOptionsPal,(u16*) BG_PALETTE_SUB,256*2); + unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32); + dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); + + idx=0; + while (true) + { + sprintf(strBuf, " %-12s : %-12s ", Option_Table[idx].label, Option_Table[idx].option[*(Option_Table[idx].option_val)]); + dsPrintValue(1,5+idx, (idx==0 ? 1:0), strBuf); + idx++; + if (Option_Table[idx].label == NULL) break; + } + + dsPrintValue(2,23, 0, (char *)"A=TOGGLE, B=SAVE+EXIT"); + optionHighlighted = 0; + while (!bDone) + { + keys_pressed = keysCurrent(); + if (keys_pressed != last_keys_pressed) + { + last_keys_pressed = keys_pressed; + if (keysCurrent() & KEY_UP) // Previous option + { + sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]); + dsPrintValue(1,5+optionHighlighted,0, strBuf); + if (optionHighlighted > 0) optionHighlighted--; else optionHighlighted=(idx-1); + sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]); + dsPrintValue(1,5+optionHighlighted,1, strBuf); + } + if (keysCurrent() & KEY_DOWN) // Next option + { + sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]); + dsPrintValue(1,5+optionHighlighted,0, strBuf); + if (optionHighlighted < (idx-1)) optionHighlighted++; else optionHighlighted=0; + sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]); + dsPrintValue(1,5+optionHighlighted,1, strBuf); + } + + if (keysCurrent() & KEY_A) // Toggle option + { + *(Option_Table[optionHighlighted].option_val) = (*(Option_Table[optionHighlighted].option_val) + 1) % Option_Table[optionHighlighted].option_max; + sprintf(strBuf, " %-12s : %-12s ", Option_Table[optionHighlighted].label, Option_Table[optionHighlighted].option[*(Option_Table[optionHighlighted].option_val)]); + dsPrintValue(1,5+optionHighlighted,1, strBuf); + } + if (keysCurrent() & KEY_START) // Save Options + { + // TODO: write them out... dsWriteConfig(); + } + if (keysCurrent() & KEY_B) // Exit options + { + break; + } + } + swiWaitForVBlank(); + } + + // Restore original bottom graphic + dsShowScreenMain(false); + + // Give a third of a second time delay... + for (int i=0; i<20; i++) + { + swiWaitForVBlank(); + } + + return; +} + +// End of Line + diff --git a/arm9/source/ds_tools.h b/arm9/source/ds_tools.h new file mode 100644 index 0000000..3d17f33 --- /dev/null +++ b/arm9/source/ds_tools.h @@ -0,0 +1,36 @@ +#ifndef __DS_TOOLS_H +#define __DS_TOOLS_H + +#include + +#define STELLADS_MENUINIT 0x01 +#define STELLADS_MENUSHOW 0x02 +#define STELLADS_PLAYINIT 0x03 +#define STELLADS_PLAYGAME 0x04 +#define STELLADS_QUITSTDS 0x05 + +extern unsigned int etatEmu; + +typedef enum { + EMUARM7_INIT_SND = 0x123C, + EMUARM7_STOP_SND = 0x123D, + EMUARM7_PLAY_SND = 0x123E, +} FifoMesType; + +typedef struct FICtoLoad { + char filename[255]; + bool directory; +} FICA2600; + +extern void dsPrintValue(int x, int y, unsigned int isSelect, char *pchStr); +extern void dsMainLoop(void); +extern void dsInstallSoundEmuFIFO(void); +extern void dsInitScreenMain(void); +extern void dsInitTimer(void); +extern void dsFreeEmu(void); +extern void dsShowScreenEmu(void); +extern bool dsWaitOnQuit(void); +extern void dsChooseOptions(void); +extern unsigned int dsWaitForRom(char *chosen_filename); + +#endif diff --git a/arm9/source/emucore/AY38900.ITCM.cpp b/arm9/source/emucore/AY38900.ITCM.cpp new file mode 100644 index 0000000..ac90176 --- /dev/null +++ b/arm9/source/emucore/AY38900.ITCM.cpp @@ -0,0 +1,1058 @@ +#include +//#include +#include "AY38900.h" +#include "ProcessorBus.h" + +#define MIN(v1, v2) (v1 < v2 ? v1 : v2) +#define MAX(v1, v2) (v1 > v2 ? v1 : v2) + +#define MODE_VBLANK 0 +#define MODE_START_ACTIVE_DISPLAY 1 +#define MODE_IDLE_ACTIVE_DISPLAY 2 +#define MODE_FETCH_ROW_0 3 +#define MODE_RENDER_ROW_0 4 +#define MODE_FETCH_ROW_1 5 +#define MODE_RENDER_ROW_1 6 +#define MODE_FETCH_ROW_2 7 +#define MODE_RENDER_ROW_2 8 +#define MODE_FETCH_ROW_3 9 +#define MODE_RENDER_ROW_3 10 +#define MODE_FETCH_ROW_4 11 +#define MODE_RENDER_ROW_4 12 +#define MODE_FETCH_ROW_5 13 +#define MODE_RENDER_ROW_5 14 +#define MODE_FETCH_ROW_6 15 +#define MODE_RENDER_ROW_6 16 +#define MODE_FETCH_ROW_7 17 +#define MODE_RENDER_ROW_7 18 +#define MODE_FETCH_ROW_8 19 +#define MODE_RENDER_ROW_8 20 +#define MODE_FETCH_ROW_9 21 +#define MODE_RENDER_ROW_9 22 +#define MODE_FETCH_ROW_10 23 +#define MODE_RENDER_ROW_10 24 +#define MODE_FETCH_ROW_11 25 +#define MODE_RENDER_ROW_11 26 +#define MODE_FETCH_ROW_12 27 + +const INT32 AY38900::TICK_LENGTH_SCANLINE = 228; +const INT32 AY38900::TICK_LENGTH_FRAME = 59736; +const INT32 AY38900::TICK_LENGTH_VBLANK = 15164; +const INT32 AY38900::TICK_LENGTH_START_ACTIVE_DISPLAY = 112; +const INT32 AY38900::TICK_LENGTH_IDLE_ACTIVE_DISPLAY = 456; +const INT32 AY38900::TICK_LENGTH_FETCH_ROW = 456; +const INT32 AY38900::TICK_LENGTH_RENDER_ROW = 3192; +const INT32 AY38900::LOCATION_BACKTAB = 0x0200; +const INT32 AY38900::LOCATION_GROM = 0x3000; +const INT32 AY38900::LOCATION_GRAM = 0x3800; +const INT32 AY38900::FOREGROUND_BIT = 0x0010; + +UINT16 mobBuffers[8][128] __attribute__((section(".dtcm"))); +UINT8 stretch[16] __attribute__((section(".dtcm"))) = {0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F, 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF}; +UINT8 reverse[16] __attribute__((section(".dtcm"))) = {0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF}; + +AY38900::AY38900(MemoryBus* mb, ROM* go, GRAM* ga) + : Processor("AY-3-8900"), + memoryBus(mb), + grom(go), + gram(ga), + backtab() +{ + registers.init(this); + + horizontalOffset = 0; + verticalOffset = 0; + blockTop = FALSE; + blockLeft = FALSE; + mode = 0; +} + +void AY38900::resetProcessor() +{ + //switch to bus copy mode + setGraphicsBusVisible(TRUE); + + //reset the mobs + for (UINT8 i = 0; i < 8; i++) + mobs[i].reset(); + + //reset the state variables + mode = -1; + pinOut[AY38900_PIN_OUT_SR1]->isHigh = TRUE; + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + previousDisplayEnabled = TRUE; + displayEnabled = FALSE; + colorStackMode = FALSE; + colorModeChanged = TRUE; + bordersChanged = TRUE; + colorStackChanged = TRUE; + offsetsChanged = TRUE; + + //local register data + borderColor = 0; + blockLeft = blockTop = FALSE; + horizontalOffset = verticalOffset = 0; +} + +void AY38900::setGraphicsBusVisible(BOOL visible) { + registers.SetEnabled(visible); + gram->SetEnabled(visible); + grom->SetEnabled(visible); +} + +INT32 AY38900::tick(INT32 minimum) { + INT32 totalTicks = 0; + do { + switch (mode) + { + //start of vertical blank + case MODE_VBLANK: + //come out of bus isolation mode + setGraphicsBusVisible(TRUE); + if (previousDisplayEnabled) + renderFrame(); + displayEnabled = FALSE; + + //start of vblank, so stop and go back to the main loop + processorBus->stop(); + + //release SR2, allowing the CPU to run + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + + //kick the irq line + pinOut[AY38900_PIN_OUT_SR1]->isHigh = FALSE; + + totalTicks += TICK_LENGTH_VBLANK; + if (totalTicks >= minimum) { + mode = MODE_START_ACTIVE_DISPLAY; + break; + } + + case MODE_START_ACTIVE_DISPLAY: + pinOut[AY38900_PIN_OUT_SR1]->isHigh = TRUE; + + //if the display is not enabled, skip the rest of the modes + if (!displayEnabled) { + if (previousDisplayEnabled) { + //render a blank screen + for (int x = 0; x < 160*192; x++) + pixelBuffer[x] = borderColor; + } + previousDisplayEnabled = FALSE; + mode = MODE_VBLANK; + totalTicks += (TICK_LENGTH_FRAME - TICK_LENGTH_VBLANK); + break; + } + else { + previousDisplayEnabled = TRUE; + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + totalTicks += TICK_LENGTH_START_ACTIVE_DISPLAY; + if (totalTicks >= minimum) { + mode = MODE_IDLE_ACTIVE_DISPLAY; + break; + } + } + + case MODE_IDLE_ACTIVE_DISPLAY: + //switch to bus isolation mode, but only if the CPU has + //acknowledged ~SR2 by asserting ~SST + if (!pinIn[AY38900_PIN_IN_SST]->isHigh) { + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + setGraphicsBusVisible(FALSE); + } + + //release SR2 + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + + totalTicks += TICK_LENGTH_IDLE_ACTIVE_DISPLAY + + (2*verticalOffset*TICK_LENGTH_SCANLINE); + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_0; + break; + } + + case MODE_FETCH_ROW_0: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_0; + break; + } + + case MODE_RENDER_ROW_0: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_1; + break; + } + + case MODE_FETCH_ROW_1: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_1; + break; + } + + case MODE_RENDER_ROW_1: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_2; + break; + } + + case MODE_FETCH_ROW_2: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_2; + break; + } + + case MODE_RENDER_ROW_2: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_3; + break; + } + + case MODE_FETCH_ROW_3: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_3; + break; + } + + case MODE_RENDER_ROW_3: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_4; + break; + } + + case MODE_FETCH_ROW_4: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_4; + break; + } + + case MODE_RENDER_ROW_4: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_5; + break; + } + + case MODE_FETCH_ROW_5: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_5; + break; + } + + case MODE_RENDER_ROW_5: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_6; + break; + } + + case MODE_FETCH_ROW_6: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_6; + break; + } + + case MODE_RENDER_ROW_6: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_7; + break; + } + + case MODE_FETCH_ROW_7: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_7; + break; + } + + case MODE_RENDER_ROW_7: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_8; + break; + } + + case MODE_FETCH_ROW_8: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_8; + break; + } + + case MODE_RENDER_ROW_8: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_9; + break; + } + + case MODE_FETCH_ROW_9: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_9; + break; + } + + case MODE_RENDER_ROW_9: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_10; + break; + } + + case MODE_FETCH_ROW_10: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_10; + break; + } + + case MODE_RENDER_ROW_10: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + pinIn[AY38900_PIN_IN_SST]->isHigh = TRUE; + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_11; + break; + } + + case MODE_FETCH_ROW_11: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + //renderRow((mode-3)/2); + totalTicks += TICK_LENGTH_FETCH_ROW; + if (totalTicks >= minimum) { + mode = MODE_RENDER_ROW_11; + break; + } + + case MODE_RENDER_ROW_11: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = TRUE; + + //this mode could be cut off in tick length if the vertical + //offset is greater than 1 + if (verticalOffset == 0) { + totalTicks += TICK_LENGTH_RENDER_ROW; + if (totalTicks >= minimum) { + mode = MODE_FETCH_ROW_12; + break; + } + } + else if (verticalOffset == 1) { + totalTicks += TICK_LENGTH_RENDER_ROW - TICK_LENGTH_SCANLINE; + mode = MODE_VBLANK; + break; + } + else { + totalTicks += (TICK_LENGTH_RENDER_ROW - TICK_LENGTH_SCANLINE + - (2 * (verticalOffset - 1) * TICK_LENGTH_SCANLINE)); + mode = MODE_VBLANK; + break; + } + + case MODE_FETCH_ROW_12: + default: + pinOut[AY38900_PIN_OUT_SR2]->isHigh = FALSE; + totalTicks += TICK_LENGTH_SCANLINE; + mode = MODE_VBLANK; + break; + } + } while (totalTicks < minimum); + + return totalTicks; +} + +void AY38900::setPixelBuffer(UINT8* pixelBuffer, UINT32 rowSize) +{ + AY38900::pixelBuffer = pixelBuffer; + AY38900::pixelBufferRowSize = rowSize; +} + +ITCM_CODE void AY38900::renderFrame() +{ + //render the next frame + renderBackground(); + renderMOBs(); + for (int i = 0; i < 8; i++) + mobs[i].collisionRegister = 0; + determineMOBCollisions(); + markClean(); + renderBorders(); + copyBackgroundBufferToStagingArea(); + copyMOBsToStagingArea(); + for (int i = 0; i < 8; i++) + memory[0x18+i] |= mobs[i].collisionRegister; +} + +ITCM_CODE void AY38900::render() +{ + // the video bus handles the actual rendering. +} + +ITCM_CODE void AY38900::markClean() { + //everything has been rendered and is now clean + offsetsChanged = FALSE; + bordersChanged = FALSE; + colorStackChanged = FALSE; + colorModeChanged = FALSE; + backtab.markClean(); + gram->markClean(); + for (int i = 0; i < 8; i++) + mobs[i].markClean(); +} + +ITCM_CODE void AY38900::renderBorders() +{ + static int dampen_border_render = 0; + + if (++dampen_border_render & 3) return; + + //draw the top and bottom borders + if (blockTop) { + //move the image up 4 pixels and block the top and bottom 4 rows with the border + for (UINT8 y = 0; y < 8; y++) { + UINT8* buffer0 = ((UINT8*)pixelBuffer) + (y*pixelBufferRowSize); + UINT8* buffer1 = buffer0 + (184*pixelBufferRowSize); + for (UINT8 x = 0; x < 160; x++) { + *buffer0++ = borderColor; + *buffer1++ = borderColor; + } + } + } + else if (verticalOffset != 0) { + //block the top rows of pixels depending on the amount of vertical offset + UINT8 numRows = (UINT8)(verticalOffset<<1); + for (UINT8 y = 0; y < numRows; y++) { + UINT8* buffer0 = ((UINT8*)pixelBuffer) + (y*pixelBufferRowSize); + for (UINT8 x = 0; x < 160; x++) + *buffer0++ = borderColor; + } + } + + //draw the left and right borders + if (blockLeft) { + //move the image to the left 4 pixels and block the left and right 4 columns with the border + for (UINT8 y = 0; y < 192; y++) { + UINT8* buffer0 = ((UINT8*)pixelBuffer) + (y*pixelBufferRowSize); + UINT8* buffer1 = buffer0 + 156; + for (UINT8 x = 0; x < 4; x++) { + *buffer0++ = borderColor; + *buffer1++ = borderColor; + } + } + } + else if (horizontalOffset != 0) { + //block the left columns of pixels depending on the amount of horizontal offset + for (UINT8 y = 0; y < 192; y++) { + UINT8* buffer0 = ((UINT8*)pixelBuffer) + (y*pixelBufferRowSize); + for (UINT8 x = 0; x < horizontalOffset; x++) { + *buffer0++ = borderColor; + } + } + } +} + +ITCM_CODE void AY38900::renderMOBs() +{ + for (int i = 0; i < 8; i++) + { + //if the mob did not change shape and it's rendering from GROM (indicating that + //the source of its rendering could not have changed), then this MOB does not need + //to be re-rendered into its buffer + if (!mobs[i].shapeChanged && mobs[i].isGrom) + continue; + + //start at this memory location + UINT16 firstMemoryLocation = (UINT16)(mobs[i].isGrom + ? LOCATION_GROM + (mobs[i].cardNumber << 3) + : LOCATION_GRAM + ((mobs[i].cardNumber & 0x3F) << 3)); + + //end at this memory location + UINT16 lastMemoryLocation = (UINT16)(firstMemoryLocation + 8); + if (mobs[i].doubleYResolution) + lastMemoryLocation += 8; + + //make the pixels this tall + int pixelHeight = (mobs[i].quadHeight ? 4 : 1) * (mobs[i].doubleHeight ? 2 : 1); + + //start at the first line for regular vertical rendering or start at the last line + //for vertically mirrored rendering + int nextLine = 0; + if (mobs[i].verticalMirror) + nextLine = (pixelHeight * (mobs[i].doubleYResolution ? 15 : 7)); + for (UINT16 j = firstMemoryLocation; j < lastMemoryLocation; j++) { + if (!mobs[i].shapeChanged && !gram->isCardDirty((UINT16)(j & 0x01FF))) { + if (mobs[i].verticalMirror) + nextLine -= pixelHeight; + else + nextLine += pixelHeight; + continue; + } + + //get the next line of pixels + UINT16 nextData = (UINT16)(memoryBus->peek(j) & 0xFF); + + //reverse the pixels horizontally if necessary + if (mobs[i].horizontalMirror) + nextData = (UINT16)((reverse[nextData & 0x0F] << 4) | reverse[(nextData & 0xF0) >> 4]); + + //double them if necessary + if (mobs[i].doubleWidth) + nextData = (UINT16)((stretch[(nextData & 0xF0) >> 4] << 8) | stretch[nextData & 0x0F]); + else + nextData <<= 8; + + //lay down as many lines of pixels as necessary + for (int k = 0; k < pixelHeight; k++) + mobBuffers[i][nextLine++] = nextData; + + if (mobs[i].verticalMirror) + nextLine -= (2*pixelHeight); + } + } +} + +ITCM_CODE void AY38900::renderBackground() +{ + /* + if (!backtab.isDirty() && !gram->isDirty() && !colorStackChanged && !colorModeChanged) + return; + */ + + if (colorStackMode) + renderColorStackMode(); + else + renderForegroundBackgroundMode(); +} + +ITCM_CODE void AY38900::renderForegroundBackgroundMode() +{ + //iterate through all the cards in the backtab + for (UINT8 i = 0; i < 240; i++) { + //get the next card to render + UINT16 nextCard = backtab.peek(LOCATION_BACKTAB+i); + BOOL isGrom = (nextCard & 0x0800) == 0; + UINT16 memoryLocation = nextCard & 0x01F8; + + //render this card only if this card has changed or if the card points to GRAM + //and one of the eight bytes in gram that make up this card have changed + if (colorModeChanged || backtab.isDirty(LOCATION_BACKTAB+i) || (!isGrom && gram->isCardDirty(memoryLocation))) { + UINT8 fgcolor = (UINT8)((nextCard & 0x0007) | FOREGROUND_BIT); + UINT8 bgcolor = (UINT8)(((nextCard & 0x2000) >> 11) | ((nextCard & 0x1600) >> 9)); + + Memory* memory = (isGrom ? (Memory*)grom : (Memory*)gram); + UINT16 address = memory->getReadAddress()+memoryLocation; + UINT8 nextx = (i%20) * 8; + UINT8 nexty = (i/20) * 8; + for (UINT16 j = 0; j < 8; j++) + renderLine((UINT8)memory->peek(address+j), nextx, nexty+j, fgcolor, bgcolor); + } + } +} + +ITCM_CODE void AY38900::renderColorStackMode() +{ + UINT8 csPtr = 0; + //if there are any dirty color advance bits in the backtab, or if + //the color stack or the color mode has changed, the whole scene + //must be rendered + BOOL renderAll = backtab.areColorAdvanceBitsDirty() || + colorStackChanged || colorModeChanged; + + UINT8 nextx = 0; + UINT8 nexty = 0; + //iterate through all the cards in the backtab + for (UINT8 h = 0; h < 240; h++) + { + UINT16 nextCard = backtab.peek(LOCATION_BACKTAB+h); + + //colored squares mode + if ((nextCard & 0x1800) == 0x1000) { + if (renderAll || backtab.isDirty(LOCATION_BACKTAB+h)) { + UINT8 csColor = (UINT8)memory[0x28 + csPtr]; + UINT8 color0 = (UINT8)(nextCard & 0x0007); + UINT8 color1 = (UINT8)((nextCard & 0x0038) >> 3); + UINT8 color2 = (UINT8)((nextCard & 0x01C0) >> 6); + UINT8 color3 = (UINT8)(((nextCard & 0x2000) >> 11) | + ((nextCard & 0x0600) >> 9)); + renderColoredSquares(nextx, nexty, + (color0 == 7 ? csColor : (UINT8)(color0 | FOREGROUND_BIT)), + (color1 == 7 ? csColor : (UINT8)(color1 | FOREGROUND_BIT)), + (color2 == 7 ? csColor : (UINT8)(color2 | FOREGROUND_BIT)), + (color3 == 7 ? csColor : (UINT8)(color3 | FOREGROUND_BIT))); + } + } + //color stack mode + else { + //advance the color pointer, if necessary + if ((nextCard & 0x2000) != 0) + csPtr = (UINT8)((csPtr+1) & 0x03); + + BOOL isGrom = (nextCard & 0x0800) == 0; + UINT16 memoryLocation = (isGrom ? (nextCard & 0x07F8) + : (nextCard & 0x01F8)); + + if (renderAll || backtab.isDirty(LOCATION_BACKTAB+h) || + (!isGrom && gram->isCardDirty(memoryLocation))) { + UINT8 fgcolor = (UINT8)(((nextCard & 0x1000) >> 9) | + (nextCard & 0x0007) | FOREGROUND_BIT); + UINT8 bgcolor = (UINT8)memory[0x28 + csPtr]; + Memory* memory = (isGrom ? (Memory*)grom : (Memory*)gram); + UINT16 address = memory->getReadAddress()+memoryLocation; + for (UINT16 j = 0; j < 8; j++) + renderLine((UINT8)memory->peek(address+j), nextx, nexty+j, fgcolor, bgcolor); + } + } + nextx += 8; + if (nextx == 160) { + nextx = 0; + nexty += 8; + } + } +} + +ITCM_CODE void AY38900::copyBackgroundBufferToStagingArea() +{ + int sourceWidthX = blockLeft ? 152 : (160 - horizontalOffset); + int sourceHeightY = blockTop ? 88 : (96 - verticalOffset); + + int nextSourcePixel = (blockLeft ? (8 - horizontalOffset) : 0) + + ((blockTop ? (8 - verticalOffset) : 0) * 160); + + for (int y = 0; y < sourceHeightY; y++) + { + UINT8* nextPixelStore0 = (UINT8*)pixelBuffer; + nextPixelStore0 += (y*pixelBufferRowSize*4)>>1; + if (blockTop) nextPixelStore0 += (pixelBufferRowSize*4)<<1; + if (blockLeft) nextPixelStore0 += 4; + UINT8* nextPixelStore1 = nextPixelStore0 + pixelBufferRowSize; + for (int x = 0; x < sourceWidthX; x++) { + UINT8 nextColor = backgroundBuffer[nextSourcePixel+x]; + *nextPixelStore0++ = nextColor; + *nextPixelStore1++ = nextColor; + } + nextSourcePixel += 160; + } +#if 0 + int sourceWidthX = blockLeft ? 152 : (160 - horizontalOffset); + int sourceHeightY = blockTop ? 88 : (96 - verticalOffset); + + int nextSourcePixel = (blockLeft ? (8 - horizontalOffset) : 0) + + ((blockTop ? (8 - verticalOffset) : 0) * 160); + + for (int y = 0; y < sourceHeightY; y++) { + UINT32* nextPixelStore0 = (UINT32*)pixelBuffer; + nextPixelStore0 += (y*pixelBufferRowSize)>>1; + if (blockTop) nextPixelStore0 += pixelBufferRowSize<<1; + if (blockLeft) nextPixelStore0 += 4; + UINT32* nextPixelStore1 = nextPixelStore0 + pixelBufferRowSize/4; + for (int x = 0; x < sourceWidthX; x++) { + UINT32 nextColor = backgroundBuffer[nextSourcePixel+x]; + *nextPixelStore0++ = nextColor; + *nextPixelStore1++ = nextColor; + } + nextSourcePixel += 160; + } +#endif +} + +//copy the offscreen mob buffers to the staging area +ITCM_CODE void AY38900::copyMOBsToStagingArea() +{ + for (INT8 i = 7; i >= 0; i--) { + if (mobs[i].xLocation == 0 || (!mobs[i].flagCollisions && !mobs[i].isVisible)) + continue; + + BOOL borderCollision = FALSE; + BOOL foregroundCollision = FALSE; + + MOBRect* r = mobs[i].getBounds(); + UINT8 mobPixelHeight = (UINT8)(r->height << 1); + UINT8 fgcolor = (UINT8)mobs[i].foregroundColor; + + INT16 leftX = (INT16)(r->x + horizontalOffset); + INT16 nextY = (INT16)((r->y + verticalOffset) << 1); + for (UINT8 y = 0; y < mobPixelHeight; y++) { + for (UINT8 x = 0; x < r->width; x++) { + //if this mob pixel is not on, then our life has no meaning + if ((mobBuffers[i][y] & (0x8000 >> x)) == 0) + continue; + + //if the next pixel location is on the border, then we + //have a border collision and we can ignore painting it + int nextX = leftX + x; + if (nextX < (blockLeft ? 8 : 0) || nextX > 158 || + nextY < (blockTop ? 16 : 0) || nextY > 191) { + borderCollision = TRUE; + continue; + } + + //check for foreground collision + UINT8 currentPixel = backgroundBuffer[(r->x+x)+ ((r->y+(y/2))*160)]; + if ((currentPixel & FOREGROUND_BIT) != 0) { + foregroundCollision = TRUE; + if (mobs[i].behindForeground) + continue; + } + if (mobs[i].isVisible) { + UINT8* nextPixel = (UINT8*)pixelBuffer; + nextPixel += leftX - (blockLeft ? 4 : 0) + x; + nextPixel += (nextY - (blockTop ? 8 : 0)) * (pixelBufferRowSize); + *nextPixel = fgcolor | (currentPixel & FOREGROUND_BIT); + } + } + nextY++; + } + + //update the collision bits + if (mobs[i].flagCollisions) { + if (foregroundCollision) + mobs[i].collisionRegister |= 0x0100; + if (borderCollision) + mobs[i].collisionRegister |= 0x0200; + } + } +} + +ITCM_CODE void AY38900::renderLine(UINT8 nextbyte, int x, int y, UINT8 fgcolor, UINT8 bgcolor) +{ + UINT8* nextTargetPixel = backgroundBuffer + x + (y*160); + *nextTargetPixel++ = (nextbyte & 0x80) != 0 ? fgcolor : bgcolor; + *nextTargetPixel++ = (nextbyte & 0x40) != 0 ? fgcolor : bgcolor; + *nextTargetPixel++ = (nextbyte & 0x20) != 0 ? fgcolor : bgcolor; + *nextTargetPixel++ = (nextbyte & 0x10) != 0 ? fgcolor : bgcolor; + + *nextTargetPixel++ = (nextbyte & 0x08) != 0 ? fgcolor : bgcolor; + *nextTargetPixel++ = (nextbyte & 0x04) != 0 ? fgcolor : bgcolor; + *nextTargetPixel++ = (nextbyte & 0x02) != 0 ? fgcolor : bgcolor; + *nextTargetPixel++ = (nextbyte & 0x01) != 0 ? fgcolor : bgcolor; +} + +ITCM_CODE void AY38900::renderColoredSquares(int x, int y, UINT8 color0, UINT8 color1, + UINT8 color2, UINT8 color3) { + int topLeftPixel = x + (y*160); + int topRightPixel = topLeftPixel+4; + int bottomLeftPixel = topLeftPixel+640; + int bottomRightPixel = bottomLeftPixel+4; + + for (UINT8 w = 0; w < 4; w++) { + for (UINT8 i = 0; i < 4; i++) { + backgroundBuffer[topLeftPixel++] = color0; + backgroundBuffer[topRightPixel++] = color1; + backgroundBuffer[bottomLeftPixel++] = color2; + backgroundBuffer[bottomRightPixel++] = color3; + } + topLeftPixel += 156; + topRightPixel += 156; + bottomLeftPixel += 156; + bottomRightPixel += 156; + } +} + +ITCM_CODE void AY38900::determineMOBCollisions() +{ + for (int i = 0; i < 7; i++) { + if (mobs[i].xLocation == 0 || !mobs[i].flagCollisions) + continue; + + /* + //check MOB on foreground collisions + if (mobCollidesWithForeground(i)) + mobs[i].collisionRegister |= 0x0100; + + //check MOB on border collisions + if (mobCollidesWithBorder(i)) + mobs[i].collisionRegister |= 0x0200; + */ + + //check MOB on MOB collisions + for (int j = i+1; j < 8; j++) + { + if (mobs[j].xLocation == 0 || !mobs[j].flagCollisions) + continue; + + if (mobsCollide(i, j)) { + mobs[i].collisionRegister |= (1 << j); + mobs[j].collisionRegister |= (1 << i); + } + } + } +} + +BOOL AY38900::mobCollidesWithBorder(int mobNum) +{ + MOBRect* r = mobs[mobNum].getBounds(); + UINT8 mobPixelHeight = (UINT8)(r->height<<1); + + /* + if (r->x > (blockLeft ? 8 : 0) && r->x+r->width <= 191 && + r->y > (blockTop ? 8 : 0) && r->y+r->height <= 158) + return FALSE; + + for (UINT8 i = 0; i < r->height; i++) { + if (mobBuffers[mobNum][i<<1] == 0 || mobBuffers[mobNum][(i<<1)+1] == 0) + continue; + + if (r->y+i < (blockLeft ? 8 : 0) || r->y+r->height+i > 158) + return TRUE; + + //if (r->x && border + } + */ + + UINT16 leftRightBorder = 0; + //check if could possibly touch the left border + if (r->x < (blockLeft ? 8 : 0)) { + leftRightBorder = (UINT16)((blockLeft ? 0xFFFF : 0xFF00) << mobs[mobNum].xLocation); + } + //check if could possibly touch the right border + else if (r->x+r->width > 158) { + leftRightBorder = 0xFFFF; + if (r->x < 158) + leftRightBorder >>= r->x-158; + } + + //check if touching the left or right border + if (leftRightBorder) { + for (INT32 i = 0; i < mobPixelHeight; i++) { + if ((mobBuffers[mobNum][i] & leftRightBorder) != 0) + return TRUE; + } + } + + //check if touching the top border + UINT8 overlappingStart = 0; + UINT8 overlappingHeight = 0; + if (r->y < (blockTop ? 8 : 0)) { + overlappingHeight = mobPixelHeight; + if (r->y+r->height > (blockTop ? 8 : 0)) + overlappingHeight = (UINT8)(mobPixelHeight - (2*(r->y+r->height-(blockTop ? 8 : 0)))); + } + //check if touching the bottom border + else if (r->y+r->height > 191) { + if (r->y < 191) + overlappingStart = (UINT8)(2*(191-r->y)); + overlappingHeight = mobPixelHeight - overlappingStart; + } + + if (overlappingHeight) { + for (UINT8 i = overlappingStart; i < overlappingHeight; i++) { + if (mobBuffers[mobNum][i] != 0) + return TRUE; + } + } + + return FALSE; +} + +BOOL AY38900::mobsCollide(int mobNum0, int mobNum1) +{ + MOBRect* r0 = mobs[mobNum0].getBounds(); + MOBRect* r1 = mobs[mobNum1].getBounds(); + if (!r0->intersects(r1)) + return FALSE; + + //determine the overlapping horizontal area + int startingX = MAX(r0->x, r1->x); + int offsetXr0 = startingX - r0->x; + int offsetXr1 = startingX - r1->x; + + //determine the overlapping vertical area + int startingY = MAX(r0->y, r1->y); + int offsetYr0 = (startingY - r0->y) * 2; + int offsetYr1 = (startingY - r1->y) * 2; + int overlappingHeight = (MIN(r0->y + r0->height, r1->y + r1->height) - startingY) * 2; + + //iterate over the intersecting bits to see if any touch + for (int y = 0; y < overlappingHeight; y++) { + if (((mobBuffers[mobNum0][offsetYr0 + y] << offsetXr0) & (mobBuffers[mobNum1][offsetYr1 + y] << offsetXr1)) != 0) + return TRUE; + } + + return FALSE; +} + +AY38900State AY38900::getState() +{ + AY38900State state = {0}; +#if 0 + this->registers.getMemory(state.registers, 0, this->registers.getMemoryByteSize()); + state.backtab = this->backtab.getState(); + + state.inVBlank = this->inVBlank; + state.mode = this->mode; + state.previousDisplayEnabled = this->previousDisplayEnabled; + state.displayEnabled = this->displayEnabled; + state.colorStackMode = this->colorStackMode; + + state.borderColor = this->borderColor; + state.blockLeft = this->blockLeft; + state.blockTop = this->blockTop; + state.horizontalOffset = this->horizontalOffset; + state.verticalOffset = this->verticalOffset; + + for (int i = 0; i < 8; i++) { + state.mobs[i] = this->mobs[i].getState(); + } +#endif + return state; +} + +void AY38900::setState(AY38900State state) +{ +#if 0 + this->registers.setMemory(state.registers, 0, this->registers.getMemoryByteSize()); + this->backtab.setState(state.backtab, state.backtab.image); + + this->inVBlank = state.inVBlank; + this->mode = state.mode; + this->previousDisplayEnabled = state.previousDisplayEnabled; + this->displayEnabled = state.displayEnabled; + this->colorStackMode = state.colorStackMode; + + this->borderColor = state.borderColor; + this->blockLeft = state.blockLeft; + this->blockTop = state.blockTop; + this->horizontalOffset = state.horizontalOffset; + this->verticalOffset = state.verticalOffset; + + for (int i = 0; i < 8; i++) { + mobs[i].setState(state.mobs[i]); + } + + this->colorModeChanged = TRUE; + this->bordersChanged = TRUE; + this->colorStackChanged = TRUE; + this->offsetsChanged = TRUE; + this->imageBufferChanged = TRUE; +#endif +} + +/* +void AY38900::renderRow(int rowNum) { + UINT8 backTabPtr = (UINT8)(0x200+(rowNum*20)); + if (colorStackMode) { + UINT8 csPtr = 0; + UINT8 nextx = 0; + UINT8 nexty = 0; + for (UINT8 h = 0; h < 20; h++) { + UINT8 nextCard = (UINT8)backtab.peek(backTabPtr); + backTabPtr++; + + if ((nextCard & 0x1800) == 0x1000) { + //colored squares mode + UINT8 csColor = (UINT8)registers.memory[0x28 + csPtr]; + UINT8 color0 = (UINT8)(nextCard & 0x0007); + UINT8 color1 = (UINT8)((nextCard & 0x0038) >> 3); + UINT8 color2 = (UINT8)((nextCard & 0x01C0) >> 6); + UINT8 color3 = (UINT8)(((nextCard & 0x2000) >> 11) | + ((nextCard & 0x0600) >> 9)); + renderColoredSquares(nextx, nexty, + (color0 == 7 ? csColor : (UINT8)(color0 | FOREGROUND_BIT)), + (color1 == 7 ? csColor : (UINT8)(color1 | FOREGROUND_BIT)), + (color2 == 7 ? csColor : (UINT8)(color2 | FOREGROUND_BIT)), + (color3 == 7 ? csColor : (UINT8)(color3 | FOREGROUND_BIT))); + } + else { + //color stack mode + //advance the color pointer, if necessary + if ((nextCard & 0x2000) != 0) + csPtr = (UINT8)((csPtr+1) & 0x03); + + BOOL isGrom = (nextCard & 0x0800) == 0; + UINT8 memoryLocation = (UINT8)(isGrom ? (nextCard & 0x07F8) + : (nextCard & 0x01F8)); + + UINT8 fgcolor = (UINT8)(((nextCard & 0x1000) >> 9) | + (nextCard & 0x0007) | FOREGROUND_BIT); + UINT8 bgcolor = (UINT8)registers.memory[0x28 + csPtr]; + UINT16* memory = (isGrom ? grom->image : gram->image); + for (int j = 0; j < 8; j++) + renderLine((UINT8)memory[memoryLocation+j], nextx, nexty+j, fgcolor, bgcolor); + } + nextx += 8; + if (nextx == 160) { + nextx = 0; + nexty += 8; + } + } + } + else { + UINT8 nextx = 0; + UINT8 nexty = 0; + for (UINT8 i = 0; i < 240; i++) { + UINT8 nextCard = (UINT8)backtab.peek((UINT8)(0x200+i)); + BOOL isGrom = (nextCard & 0x0800) == 0; + BOOL renderAll = backtab.isDirty((UINT8)(0x200+i)) || colorModeChanged; + UINT8 memoryLocation = (UINT8)(nextCard & 0x01F8); + + if (renderAll || (!isGrom && gram->isCardDirty(memoryLocation))) { + UINT8 fgcolor = (UINT8)((nextCard & 0x0007) | FOREGROUND_BIT); + UINT8 bgcolor = (UINT8)(((nextCard & 0x2000) >> 11) | + ((nextCard & 0x1600) >> 9)); + + UINT16* memory = (isGrom ? grom->image : gram->image); + for (int j = 0; j < 8; j++) + renderLine((UINT8)memory[memoryLocation+j], nextx, nexty+j, fgcolor, bgcolor); + } + nextx += 8; + if (nextx == 160) { + nextx = 0; + nexty += 8; + } + } + } +} +*/ diff --git a/arm9/source/emucore/AY38900.h b/arm9/source/emucore/AY38900.h new file mode 100644 index 0000000..fde13c5 --- /dev/null +++ b/arm9/source/emucore/AY38900.h @@ -0,0 +1,263 @@ + +#ifndef AY38900_H +#define AY38900_H + +#include "Processor.h" +#include "MemoryBus.h" +#include "ROM.h" +#include "VideoProducer.h" +#include "AY38900_Registers.h" +#include "MOB.h" +#include "BackTabRAM.h" +#include "GRAM.h" + +#define AY38900_PIN_IN_SST 0 +#define AY38900_PIN_OUT_SR1 0 +#define AY38900_PIN_OUT_SR2 1 + +TYPEDEF_STRUCT_PACK( _AY38900State +{ + BackTabRAMState backtab; + MOBState mobs[8]; + INT32 horizontalOffset; + INT32 verticalOffset; + INT32 mode; + UINT16 registers[0x40]; + INT8 inVBlank; + INT8 previousDisplayEnabled; + INT8 displayEnabled; + INT8 colorStackMode; + UINT8 borderColor; + INT8 blockLeft; + INT8 blockTop; + UINT8 _pad[1]; +} AY38900State; ) + +class AY38900 : public Processor, public VideoProducer +{ + + friend class AY38900_Registers; + +public: + AY38900(MemoryBus* mb, ROM* go, GRAM* ga); + + /** + * Implemented from the Processor interface. + * Returns the clock speed of the AY-3-8900, currently hardcoded to the NTSC clock + * rate of 3.579545 Mhz. + */ + INT32 getClockSpeed() { return 3579545; } + + /** + * Implemented from the Processor interface. + */ + void resetProcessor(); + + /** + * Implemented from the Processor interface. + */ + INT32 tick(INT32); + + /** + * Implemented from the VideoProducer interface. + */ + void setPixelBuffer(UINT8* pixelBuffer, UINT32 rowSize); + + /** + * Implemented from the VideoProducer interface. + */ + void render(); + + AY38900State getState(); + void setState(AY38900State state); + + //registers + AY38900_Registers registers; + BackTabRAM backtab; + +private: + void setGraphicsBusVisible(BOOL visible); + void renderFrame(); + BOOL somethingChanged(); + void markClean(); + void renderBorders(); + void renderMOBs(); + void renderBackground(); + void renderForegroundBackgroundMode(); + void renderColorStackMode(); + void copyBackgroundBufferToStagingArea(); + void copyMOBsToStagingArea(); + void renderLine(UINT8 nextByte, INT32 x, INT32 y, UINT8 fgcolor, UINT8 bgcolor); + void renderColoredSquares(INT32 x, INT32 y, UINT8 color0, UINT8 color1, UINT8 color2, UINT8 color3); + void determineMOBCollisions(); + BOOL mobsCollide(INT32 mobNum0, INT32 mobNum1); + BOOL mobCollidesWithBorder(int mobNum); + BOOL mobCollidesWithForeground(int mobNum); + //void renderRow(INT32 rowNum); + + const static INT32 TICK_LENGTH_SCANLINE; + const static INT32 TICK_LENGTH_FRAME; + const static INT32 TICK_LENGTH_VBLANK; + const static INT32 TICK_LENGTH_START_ACTIVE_DISPLAY; + const static INT32 TICK_LENGTH_IDLE_ACTIVE_DISPLAY; + const static INT32 TICK_LENGTH_FETCH_ROW; + const static INT32 TICK_LENGTH_RENDER_ROW; + const static INT32 LOCATION_BACKTAB; + const static INT32 LOCATION_GROM; + const static INT32 LOCATION_GRAM; + const static INT32 LOCATION_COLORSTACK; + const static INT32 FOREGROUND_BIT; + + MemoryBus* memoryBus; + + MOB mobs[8]; + UINT8 backgroundBuffer[160*96]; + + UINT8* pixelBuffer; + UINT32 pixelBufferRowSize; + + //memory listeners, for optimizations + ROM* grom; + GRAM* gram; + + //state info + BOOL inVBlank; + INT32 mode; + BOOL previousDisplayEnabled; + BOOL displayEnabled; + BOOL colorStackMode; + BOOL colorModeChanged; + BOOL bordersChanged; + BOOL colorStackChanged; + BOOL offsetsChanged; + BOOL imageBufferChanged; + + //register info + UINT8 borderColor; + BOOL blockLeft; + BOOL blockTop; + INT32 horizontalOffset; + INT32 verticalOffset; +}; + +#endif + +/* +#ifndef AY38900_H +#define AY38900_H + +#include "AY38900_Registers.h" +#include "BackTabRAM.h" +#include "GRAM.h" +#include "GROM.h" +#include "MOB.h" +#include "VideoProducer.h" +#include "core/types.h" +#include "core/cpu/Processor.h" +#include "core/cpu/SignalLine.h" +#include "core/memory/MemoryBus.h" +#include "services/VideoOutputDevice.h" + +#define AY38900_PIN_IN_SST 0 + +#define AY38900_PIN_OUT_SR1 0 +#define AY38900_PIN_OUT_SR2 1 + +class AY38900 : public Processor, public VideoProducer +{ + + friend class AY38900_Registers; + + public: + AY38900(MemoryBus* memoryBus); + void setGROMImage(UINT16* gromImage); + BOOL inVerticalBlank(); + + //Processor functions + void initProcessor(); + void releaseProcessor(); + void setVideoOutputDevice(IDirect3DDevice9* vod); + INT32 getClockSpeed(); + INT32 tick(); + + //VideoProducer functions + void render(); + + //registers + AY38900_Registers registers; + + static const INT32 TICK_LENGTH_SCANLINE; + static const INT32 TICK_LENGTH_FRAME; + static const INT32 TICK_LENGTH_VBLANK; + static const INT32 TICK_LENGTH_START_ACTIVE_DISPLAY; + static const INT32 TICK_LENGTH_IDLE_ACTIVE_DISPLAY; + static const INT32 TICK_LENGTH_FETCH_ROW; + static const INT32 TICK_LENGTH_RENDER_ROW; + static const INT32 LOCATION_BACKTAB; + static const INT32 LOCATION_GROM; + static const INT32 LOCATION_GRAM; + static const INT32 LOCATION_COLORSTACK; + static const INT32 FOREGROUND_BIT; + + private: + void setGraphicsBusVisible(BOOL visible); + void renderFrame(); + BOOL somethingChanged(); + void markClean(); + void renderBorders(); + void renderMOBs(); + void renderBackground(); + void copyBackgroundBufferToStagingArea(); + void renderForegroundBackgroundMode(); + void renderColorStackMode(); + void copyMOBsToStagingArea(); + void renderLine(UINT8 nextUINT8, INT32 x, INT32 y, + UINT8 fgcolor, UINT8 bgcolor); + void renderColoredSquares(INT32 x, INT32 y, UINT8 color0, UINT8 color1, + UINT8 color2, UINT8 color3); + void renderMessage(); + void renderCharacter(char c, INT32 x, INT32 y); + void determineMOBCollisions(); + BOOL mobsCollide(INT32 mobNum0, INT32 mobNum1); + + BOOL mobBuffers[8][16][128]; + MOB mobs[8]; + UINT8 backgroundBuffer[160*96]; + IDirect3DDevice9* videoOutputDevice; + IDirect3DTexture9* combinedTexture; + IDirect3DVertexBuffer9* vertexBuffer; + D3DLOCKED_RECT combinedBufferLock; + + MemoryBus* memoryBus; + + //memory listeners, for optimizations + BackTabRAM backtab; + GROM grom; + GRAM gram; + + //state info + BOOL inVBlank; + INT32 mode; + BOOL previousDisplayEnabled; + BOOL displayEnabled; + BOOL colorStackMode; + BOOL colorModeChanged; + BOOL bordersChanged; + BOOL colorStackChanged; + BOOL offsetsChanged; + BOOL imageBufferChanged; + + //register info + UINT8 borderColor; + BOOL blockLeft; + BOOL blockTop; + INT32 horizontalOffset; + INT32 verticalOffset; + + //palette + const static UINT32 palette[32]; + +}; + +#endif +*/ diff --git a/arm9/source/emucore/AY38900_Registers.cpp b/arm9/source/emucore/AY38900_Registers.cpp new file mode 100644 index 0000000..38fdd44 --- /dev/null +++ b/arm9/source/emucore/AY38900_Registers.cpp @@ -0,0 +1,198 @@ +#include +#include +#include "AY38900.h" +#include "AY38900_Registers.h" + +UINT16 memory[0x40] __attribute__((section(".dtcm"))); + +AY38900_Registers::AY38900_Registers() +: RAM(0x40, 0x0000, 0xFFFF, 0x3FFF) +{} + +void AY38900_Registers::init(AY38900* ay38900) +{ + this->ay38900 = ay38900; +} + +void AY38900_Registers::reset() +{ + memset(memory, 0, sizeof(memory)); +} + +void AY38900_Registers::poke(UINT16 location, UINT16 value) { + if (!enabled) + return; + + //incomplete decoding on writes + location &= 0x3F; + + switch(location) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + value &= 0x07FF; + { + MOB* mob = &ay38900->mobs[location]; + mob->setDoubleWidth((value & 0x0400) != 0); + mob->setVisible((value & 0x0200) != 0); + mob->setFlagCollisions((value & 0x0100) != 0); + mob->setXLocation(value & 0x00FF); + } + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + value &= 0x0FFF; + { + MOB* mob = &ay38900->mobs[location & 0x07]; + mob->setVerticalMirror((value & 0x0800) != 0); + mob->setHorizontalMirror((value & 0x0400) != 0); + mob->setQuadHeight((value & 0x0200) != 0); + mob->setDoubleHeight((value & 0x0100) != 0); + mob->setDoubleYResolution((value & 0x0080) != 0); + mob->setYLocation(value & 0x007F); + } + break; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + value &= 0x3FFF; + { + MOB* mob = &ay38900->mobs[location & 0x07]; + mob->setBehindForeground((value & 0x2000) != 0); + mob->setGROM((value & 0x0800) == 0); + mob->setCardNumber((value & 0x07F8) >> 3); + mob->setForegroundColor(((value & 0x1000) >> 9) | + (value & 0x0007)); + } + break; + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + //a MOB's own collision bit is *always* zero, even if a + //one is poked into it + value &= (~(1 << (location & 0x07))) & 0x03FF; + break; + case 0x20: + ay38900->displayEnabled = TRUE; + break; + case 0x21: + value = 0; + if (ay38900->colorStackMode) { + ay38900->colorStackMode = FALSE; + ay38900->colorModeChanged = TRUE; + } + break; + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + value &= 0x000F; + ay38900->colorStackChanged = TRUE; + break; + case 0x2C: + value &= 0x000F; + ay38900->borderColor = (UINT8)value; + ay38900->bordersChanged = TRUE; + break; + case 0x30: + value &= 0x0007; + ay38900->horizontalOffset = value; + ay38900->offsetsChanged = TRUE; + break; + case 0x31: + value &= 0x0007; + ay38900->verticalOffset = value; + ay38900->offsetsChanged = TRUE; + break; + case 0x32: + value &= 0x0003; + ay38900->blockLeft = (value & 0x0001) != 0; + ay38900->blockTop = (value & 0x0002) != 0; + ay38900->bordersChanged = TRUE; + break; + default: // 0x22 - 0x27 + value = 0; + break; + } + memory[location] = value; +} + +UINT16 AY38900_Registers::peek(UINT16 location) +{ + if (!enabled) + return location; + location &= 0x3F; + + switch (location) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + return 0x3800 | memory[location]; + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + return 0x3000 | memory[location]; + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + //collision register bits $3C00 are always on + return 0x3C00 | memory[location]; + case 0x20: + return 0; + case 0x21: + if (location == 0x0021 && !ay38900->colorStackMode) { + ay38900->colorStackMode = TRUE; + ay38900->colorModeChanged = TRUE; + } + return memory[location]; + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + return 0x3FF0 | memory[location]; + case 0x30: + case 0x31: + return 0x3FF8 | memory[location]; + case 0x32: + return 0x3FFC | memory[location]; + default: + return memory[location]; + } +} diff --git a/arm9/source/emucore/AY38900_Registers.h b/arm9/source/emucore/AY38900_Registers.h new file mode 100644 index 0000000..7f8a755 --- /dev/null +++ b/arm9/source/emucore/AY38900_Registers.h @@ -0,0 +1,37 @@ + +#ifndef AY38900_REGISTERS_H +#define AY38900_REGISTERS_H + +#include "RAM.h" + +class AY38900; + +extern UINT16 memory[0x40]; +class AY38900_Registers : public RAM +{ + friend class AY38900; + + public: + void reset(); + + void poke(UINT16 location, UINT16 value); + UINT16 peek(UINT16 location); + + inline size_t getMemoryByteSize() { + return sizeof(memory); + } + void getMemory(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, memory + offset, size); + } + void setMemory(void* src, UINT16 offset, UINT16 size) { + memcpy(memory + offset, src, size); + } + + private: + AY38900_Registers(); + void init(AY38900* ay38900); + + AY38900* ay38900; +}; + +#endif diff --git a/arm9/source/emucore/AY38914.ITCM.cpp b/arm9/source/emucore/AY38914.ITCM.cpp new file mode 100644 index 0000000..e5b6053 --- /dev/null +++ b/arm9/source/emucore/AY38914.ITCM.cpp @@ -0,0 +1,339 @@ +#include +#include "AY38914.h" +#include "AudioMixer.h" + +INT32 amplitudes16Bit[16] __attribute__((section(".dtcm"))) = +{ + 0x003C, 0x0055, 0x0079, 0x00AB, 0x00F1, 0x0155, 0x01E3, 0x02AA, + 0x03C5, 0x0555, 0x078B, 0x0AAB, 0x0F16, 0x1555, 0x1E2B, 0x2AAA +}; + +struct Channel_t channel0 __attribute__((section(".dtcm"))); +struct Channel_t channel1 __attribute__((section(".dtcm"))); +struct Channel_t channel2 __attribute__((section(".dtcm"))); + + INT32 clockDivisor __attribute__((section(".dtcm"))); + + //cached total output sample + BOOL cachedTotalOutputIsDirty __attribute__((section(".dtcm"))); + INT32 cachedTotalOutput __attribute__((section(".dtcm"))); + + //envelope data + BOOL envelopeIdle __attribute__((section(".dtcm"))); + INT32 envelopePeriod __attribute__((section(".dtcm"))); + INT32 envelopePeriodValue __attribute__((section(".dtcm"))); + INT32 envelopeVolume __attribute__((section(".dtcm"))); + BOOL envelopeHold __attribute__((section(".dtcm"))); + BOOL envelopeAltr __attribute__((section(".dtcm"))); + BOOL envelopeAtak __attribute__((section(".dtcm"))); + BOOL envelopeCont __attribute__((section(".dtcm"))); + INT32 envelopeCounter __attribute__((section(".dtcm"))); + + //noise data + BOOL noiseIdle __attribute__((section(".dtcm"))); + INT32 noisePeriod __attribute__((section(".dtcm"))); + INT32 noisePeriodValue __attribute__((section(".dtcm"))); + INT32 noiseCounter __attribute__((section(".dtcm"))); + + //data for random number generator, used for white noise accuracy + INT32 my_random __attribute__((section(".dtcm"))); + BOOL noise __attribute__((section(".dtcm"))); + + + +/** + * The AY-3-8914 chip in the Intellivision, also know as the Programmable + * Sound Generator (PSG). + * + * @author Kyle Davis + */ +AY38914::AY38914(UINT16 location, AY38914_InputOutput* io0, + AY38914_InputOutput* io1) + : Processor("AY-3-8914"), + registers(location) +{ + this->psgIO0 = io0; + this->psgIO1 = io1; + clockDivisor = 8; //TODO: was 1... trying to speed thigns up by lowering sound quality... + registers.init(this); +} + +INT32 AY38914::getClockSpeed() { + return 3579545; +} + +INT32 AY38914::getClocksPerSample() { + return clockDivisor<<4; +} + +void AY38914::resetProcessor() +{ + //reset the state variables + noisePeriod = 0; + noisePeriodValue = 0x0040; + noiseCounter = noisePeriodValue; + my_random = 1; + noise = TRUE; + noiseIdle = TRUE; + envelopeIdle = TRUE; + envelopePeriod = 0; + envelopePeriodValue = 0x20000; + envelopeCounter = envelopePeriodValue; + envelopeVolume = 0; + envelopeHold = FALSE; + envelopeAltr = FALSE; + envelopeAtak = FALSE; + envelopeCont = FALSE; + cachedTotalOutput = 0; + cachedTotalOutputIsDirty = TRUE; + + memset(&channel0, 0x00, sizeof(channel0)); + memset(&channel1, 0x00, sizeof(channel1)); + memset(&channel2, 0x00, sizeof(channel2)); + + channel0.periodValue = 0x1000; + channel0.toneCounter = 0x1000; + channel0.isDirty = TRUE; + + channel1.periodValue = 0x1000; + channel1.toneCounter = 0x1000; + channel1.isDirty = TRUE; + + channel2.periodValue = 0x1000; + channel2.toneCounter = 0x1000; + channel2.isDirty = TRUE; +} + +void AY38914::setClockDivisor(INT32 clockDivisor) { + clockDivisor = clockDivisor; +} + +INT32 AY38914::getClockDivisor() { + return clockDivisor; +} + +/** + * Tick the AY38914. This method has been profiled over and over again + * in an attempt to tweak it for optimal performance. Please be careful + * with any modifications that may adversely affect performance. + * + * @return the number of ticks used by the AY38914, always returns 4. + */ +ITCM_CODE INT32 AY38914::tick(INT32 minimum) +{ + INT32 totalTicks = 0; + do { + //iterate the envelope generator + envelopeCounter -= clockDivisor; + if (envelopeCounter <= 0) { + do { + envelopeCounter += envelopePeriodValue; + if (!envelopeIdle) { + envelopeVolume += (envelopeAtak ? 1 : -1); + if (envelopeVolume > 15 || envelopeVolume < 0) { + if (!envelopeCont) { + envelopeVolume = 0; + envelopeIdle = TRUE; + } + else if (envelopeHold) { + envelopeVolume = (envelopeAtak == envelopeAltr ? 0 : 15); + envelopeIdle = TRUE; + } + else { + envelopeAtak = (envelopeAtak != envelopeAltr); + envelopeVolume = (envelopeAtak ? 0 : 15); + } + } + } + } + while (envelopeCounter <= 0); + + //the envelope volume has changed so the channel outputs + //need to be updated if they are using the envelope + channel0.isDirty = channel0.envelope; + channel1.isDirty = channel1.envelope; + channel2.isDirty = channel2.envelope; + } + + //iterate the noise generator + noiseCounter -= clockDivisor; + if (noiseCounter <= 0) { + BOOL oldNoise = noise; + do { + noiseCounter += noisePeriodValue; + if (!noiseIdle) { + my_random = (my_random >> 1) ^ (noise ? 0x14000 : 0); + noise = (my_random & 1); + } + } + while (noiseCounter <= 0); + + //if the noise bit changed, then our channel outputs need + //to be updated if they are using the noise generator + if (!noiseIdle && oldNoise != noise) { + channel0.isDirty = (channel0.isDirty | !channel0.noiseDisabled); + channel1.isDirty = (channel1.isDirty | !channel1.noiseDisabled); + channel2.isDirty = (channel2.isDirty | !channel2.noiseDisabled); + } + } + + //iterate the tone generator for channel 0 + channel0.toneCounter -= clockDivisor; + if (channel0.toneCounter <= 0) { + do { + channel0.toneCounter += channel0.periodValue; + channel0.tone = !channel0.tone; + } + while (channel0.toneCounter <= 0); + + channel0.isDirty = !channel0.toneDisabled; + } + + //iterate the tone generator for channel 1 + channel1.toneCounter -= clockDivisor; + if (channel1.toneCounter <= 0) { + do { + channel1.toneCounter += channel1.periodValue; + channel1.tone = !channel1.tone; + } + while (channel1.toneCounter <= 0); + + channel1.isDirty = !channel1.toneDisabled; + } + + //iterate the tone generator for channel 2 + channel2.toneCounter -= clockDivisor; + if (channel2.toneCounter <= 0) { + do { + channel2.toneCounter += channel2.periodValue; + channel2.tone = !channel2.tone; + } + while (channel2.toneCounter <= 0); + + channel2.isDirty = !channel2.toneDisabled; + } + + if (channel0.isDirty) { + channel0.isDirty = FALSE; + channel0.cachedSample = amplitudes16Bit[ + (((channel0.toneDisabled | channel0.tone) & + (channel0.noiseDisabled | noise)) + ? (channel0.envelope ? envelopeVolume + : channel0.volume) : 0)]; + cachedTotalOutputIsDirty = TRUE; + } + + if (channel1.isDirty) { + channel1.isDirty = FALSE; + channel1.cachedSample = amplitudes16Bit[ + (((channel1.toneDisabled | channel1.tone) & + (channel1.noiseDisabled | noise)) + ? (channel1.envelope ? envelopeVolume + : channel1.volume) : 0)]; + cachedTotalOutputIsDirty = TRUE; + } + + if (channel2.isDirty) { + channel2.isDirty = FALSE; + channel2.cachedSample = amplitudes16Bit[ + (((channel2.toneDisabled | channel2.tone) & + (channel2.noiseDisabled | noise)) + ? (channel2.envelope ? envelopeVolume + : channel2.volume) : 0)]; + cachedTotalOutputIsDirty = TRUE; + } + + //mix all three channel samples together to generate the overall + //output sample for the entire AY38914 + if (cachedTotalOutputIsDirty) { + cachedTotalOutputIsDirty = FALSE; + cachedTotalOutput = (channel0.cachedSample + + channel1.cachedSample + channel2.cachedSample); + + //apply the saturation clipping to correctly model the + //cross-channel modulation that occurs on a real Intellivision + cachedTotalOutput <<= 1; + if (cachedTotalOutput > 0x6000) + cachedTotalOutput = 0x6000 + ((cachedTotalOutput - 0x6000)/6); + } + + audioOutputLine->playSample((INT16)cachedTotalOutput); + + totalTicks += (clockDivisor<<4); + + } while (totalTicks < minimum); + + //every tick here always uses some multiple of 4 CPU cycles + //or 16 NTSC cycles + return totalTicks; +} + +AY38914State AY38914::getState() +{ + AY38914State state = {0}; +#if 0 + this->registers.getMemory(state.registers, 0, this->registers.getMemoryByteSize()); + + state.clockDivisor = this->clockDivisor; + + state.channel0 = this->channel0.getState(); + state.channel1 = this->channel1.getState(); + state.channel2 = this->channel2.getState(); + + state.cachedTotalOutputIsDirty = this->cachedTotalOutputIsDirty; + state.cachedTotalOutput = this->cachedTotalOutput; + + state.envelopeIdle = this->envelopeIdle; + state.envelopePeriod = this->envelopePeriod; + state.envelopePeriodValue = this->envelopePeriodValue; + state.envelopeCounter = this->envelopeCounter; + state.envelopeVolume = this->envelopeVolume; + state.envelopeHold = this->envelopeHold; + state.envelopeAltr = this->envelopeAltr; + state.envelopeAtak = this->envelopeAtak; + state.envelopeCont = this->envelopeCont; + + state.noiseIdle = this->noiseIdle; + state.noisePeriod = this->noisePeriod; + state.noisePeriodValue = this->noisePeriodValue; + state.noiseCounter = this->noiseCounter; + + state.my_random = this->my_random; + state.noise = this->noise; +#endif + return state; +} + +void AY38914::setState(AY38914State state) +{ +#if 0 + this->registers.setMemory(state.registers, 0, this->registers.getMemoryByteSize()); + + this->clockDivisor = state.clockDivisor; + + this->channel0.setState(state.channel0); + this->channel1.setState(state.channel1); + this->channel2.setState(state.channel2); + + this->cachedTotalOutputIsDirty = state.cachedTotalOutputIsDirty; + this->cachedTotalOutput = state.cachedTotalOutput; + + this->envelopeIdle = state.envelopeIdle; + this->envelopePeriod = state.envelopePeriod; + this->envelopePeriodValue = state.envelopePeriodValue; + this->envelopeCounter = state.envelopeCounter; + this->envelopeVolume = state.envelopeVolume; + this->envelopeHold = state.envelopeHold; + this->envelopeAltr = state.envelopeAltr; + this->envelopeAtak = state.envelopeAtak; + this->envelopeCont = state.envelopeCont; + + this->noiseIdle = state.noiseIdle; + this->noisePeriod = state.noisePeriod; + this->noisePeriodValue = state.noisePeriodValue; + this->noiseCounter = state.noiseCounter; + + this->my_random = state.my_random; + this->noise = state.noise; +#endif +} diff --git a/arm9/source/emucore/AY38914.h b/arm9/source/emucore/AY38914.h new file mode 100644 index 0000000..c56720c --- /dev/null +++ b/arm9/source/emucore/AY38914.h @@ -0,0 +1,101 @@ + +#ifndef AY38914_H +#define AY38914_H + +#include "AudioProducer.h" +#include "AY38914_Registers.h" +#include "AY38914_Channel.h" +#include "AY38914_InputOutput.h" +#include "AudioOutputLine.h" +#include "Processor.h" +#include "types.h" + +class Intellivision; + +TYPEDEF_STRUCT_PACK( _AY38914State +{ + UINT16 registers[0x0E]; + INT32 clockDivisor; + INT32 cachedTotalOutput; + INT32 envelopePeriod; + INT32 envelopePeriodValue; + INT32 envelopeCounter; + INT32 envelopeVolume; + INT32 noisePeriod; + INT32 noisePeriodValue; + INT32 noiseCounter; + INT32 random; + INT8 cachedTotalOutputIsDirty; + INT8 envelopeIdle; + INT8 envelopeHold; + INT8 envelopeAltr; + INT8 envelopeAtak; + INT8 envelopeCont; + INT8 noiseIdle; + INT8 noise; +} AY38914State; ) + + +//divisor for slowing down the clock speed for the AY38914 +extern INT32 clockDivisor; + +//cached total output sample +extern BOOL cachedTotalOutputIsDirty; +extern INT32 cachedTotalOutput; + +//envelope data +extern BOOL envelopeIdle; +extern INT32 envelopePeriod; +extern INT32 envelopePeriodValue; +extern INT32 envelopeVolume; +extern BOOL envelopeHold; +extern BOOL envelopeAltr; +extern BOOL envelopeAtak; +extern BOOL envelopeCont; +extern INT32 envelopeCounter; + +//noise data +extern BOOL noiseIdle; +extern INT32 noisePeriod; +extern INT32 noisePeriodValue; +extern INT32 noiseCounter; + +//data for random number generator, used for white noise accuracy +extern INT32 my_random; +extern BOOL noise; + + +/** + * The AY-3-8914 chip in the Intellivision, also known as the Programmable + * Sound Generator (PSG). + * + * @author Kyle Davis + */ +class AY38914 : public Processor, public AudioProducer +{ + friend class AY38914_Registers; + + public: + AY38914(UINT16 location, AY38914_InputOutput* io0, + AY38914_InputOutput* io1); + void resetProcessor(); + INT32 getClockSpeed(); + INT32 getClocksPerSample(); + INT32 getSampleRate() { return getClockSpeed(); } + INT32 tick(INT32); + + void setClockDivisor(INT32 clockDivisor); + INT32 getClockDivisor(); + + AY38914State getState(); + void setState(AY38914State state); + + //registers + AY38914_Registers registers; + + private: + AY38914_InputOutput* psgIO0; + AY38914_InputOutput* psgIO1; +}; + +#endif diff --git a/arm9/source/emucore/AY38914_Channel.cpp b/arm9/source/emucore/AY38914_Channel.cpp new file mode 100644 index 0000000..542d1d0 --- /dev/null +++ b/arm9/source/emucore/AY38914_Channel.cpp @@ -0,0 +1 @@ +// Obsoleted... diff --git a/arm9/source/emucore/AY38914_Channel.h b/arm9/source/emucore/AY38914_Channel.h new file mode 100644 index 0000000..d40664c --- /dev/null +++ b/arm9/source/emucore/AY38914_Channel.h @@ -0,0 +1,25 @@ + +#ifndef AY38914__CHANNEL_H +#define AY38914__CHANNEL_H + +#include "types.h" + +struct Channel_t +{ + INT32 period; + INT32 periodValue; + INT32 volume; + INT32 toneCounter; + BOOL tone; + BOOL envelope; + BOOL toneDisabled; + BOOL noiseDisabled; + BOOL isDirty; + INT32 cachedSample; +}; + +extern struct Channel_t channel0; +extern struct Channel_t channel1; +extern struct Channel_t channel2; + +#endif diff --git a/arm9/source/emucore/AY38914_InputOutput.h b/arm9/source/emucore/AY38914_InputOutput.h new file mode 100644 index 0000000..f1629bb --- /dev/null +++ b/arm9/source/emucore/AY38914_InputOutput.h @@ -0,0 +1,16 @@ + +#ifndef AY38914INPUTOUTPUT_H +#define AY38914INPUTOUTPUT_H + +#include "types.h" + +class AY38914_InputOutput +{ + + public: + virtual UINT16 getInputValue() = 0; + virtual void setOutputValue(UINT16 value) = 0; + +}; + +#endif diff --git a/arm9/source/emucore/AY38914_Registers.cpp b/arm9/source/emucore/AY38914_Registers.cpp new file mode 100644 index 0000000..b8c41ef --- /dev/null +++ b/arm9/source/emucore/AY38914_Registers.cpp @@ -0,0 +1,180 @@ + +#include +#include +#include "AY38914.h" +#include "AY38914_Registers.h" + +AY38914_Registers::AY38914_Registers(UINT16 address) +: RAM(0x10, address, 0xFFFF, 0xFFFF) +{} + +void AY38914_Registers::init(AY38914* ay38914) +{ + this->ay38914 = ay38914; +} + +void AY38914_Registers::reset() +{ + memset(memory, 0, sizeof(memory)); +} + +void AY38914_Registers::poke(UINT16 location, UINT16 value) +{ + location &= 0x0F; + switch(location) { + case 0x00: + value = value & 0x00FF; + channel0.period = (channel0.period & 0x0F00) | + value; + channel0.periodValue = (channel0.period + ? channel0.period : 0x1000); + memory[location] = value; + break; + + case 0x01: + value = value & 0x00FF; + channel1.period = (channel1.period & 0x0F00) | + value; + channel1.periodValue = (channel1.period + ? channel1.period : 0x1000); + memory[location] = value; + break; + + case 0x02: + value = value & 0x00FF; + channel2.period = (channel2.period & 0x0F00) | + value; + channel2.periodValue = (channel2.period + ? channel2.period : 0x1000); + memory[location] = value; + break; + + case 0x03: + value = value & 0x00FF; + envelopePeriod = (envelopePeriod & 0xFF00) | + value; + envelopePeriodValue = (envelopePeriod + ? (envelopePeriod << 1) : 0x20000); + memory[location] = value; + break; + + case 0x04: + value = value & 0x000F; + channel0.period = (channel0.period & 0x00FF) | + (value<<8); + channel0.periodValue = (channel0.period + ? channel0.period : 0x1000); + memory[location] = value; + break; + + case 0x05: + value = value & 0x000F; + channel1.period = (channel1.period & 0x00FF) | + (value<<8); + channel1.periodValue = (channel1.period + ? channel1.period : 0x1000); + memory[location] = value; + break; + + case 0x06: + value = value & 0x000F; + channel2.period = (channel2.period & 0x00FF) | + (value<<8); + channel2.periodValue = (channel2.period + ? channel2.period : 0x1000); + memory[location] = value; + break; + + case 0x07: + value = value & 0x00FF; + envelopePeriod = (envelopePeriod & 0x00FF) | + (value<<8); + envelopePeriodValue = (envelopePeriod + ? (envelopePeriod << 1) : 0x20000); + memory[location] = value; + break; + + case 0x08: + value = value & 0x00FF; + channel0.toneDisabled = !!(value & 0x0001); + channel1.toneDisabled = !!(value & 0x0002); + channel2.toneDisabled = !!(value & 0x0004); + channel0.noiseDisabled = !!(value & 0x0008); + channel1.noiseDisabled = !!(value & 0x0010); + channel2.noiseDisabled = !!(value & 0x0020); + channel0.isDirty = TRUE; + channel1.isDirty = TRUE; + channel2.isDirty = TRUE; + noiseIdle = channel0.noiseDisabled & + channel1.noiseDisabled & + channel2.noiseDisabled; + memory[location] = value; + break; + + case 0x09: + value = value & 0x001F; + noisePeriod = value; + noisePeriodValue = (noisePeriod + ? (noisePeriod << 1) : 0x0040); + memory[location] = value; + break; + + case 0x0A: + value = value & 0x000F; + envelopeHold = !!(value & 0x0001); + envelopeAltr = !!(value & 0x0002); + envelopeAtak = !!(value & 0x0004); + envelopeCont = !!(value & 0x0008); + envelopeVolume = (envelopeAtak ? 0 : 15); + envelopeCounter = envelopePeriodValue; + envelopeIdle = FALSE; + memory[location] = value; + break; + + case 0x0B: + value = value & 0x003F; + channel0.envelope = !!(value & 0x0010); + channel0.volume = (value & 0x000F); + channel0.isDirty = TRUE; + memory[location] = value; + break; + + case 0x0C: + value = value & 0x003F; + channel1.envelope = !!(value & 0x0010); + channel1.volume = (value & 0x000F); + channel1.isDirty = TRUE; + memory[location] = value; + break; + + case 0x0D: + value = value & 0x003F; + channel2.envelope = !!(value & 0x0010); + channel2.volume = (value & 0x000F); + channel2.isDirty = TRUE; + memory[location] = value; + break; + + case 0x0E: + ay38914->psgIO1->setOutputValue(value); + break; + + case 0x0F: + ay38914->psgIO0->setOutputValue(value); + break; + } +} + +UINT16 AY38914_Registers::peek(UINT16 location) +{ + location &= 0x0F; + switch(location) { + case 0x0E: + return ay38914->psgIO1->getInputValue(); + case 0x0F: + return ay38914->psgIO0->getInputValue(); + default: + return memory[location]; + } +} + diff --git a/arm9/source/emucore/AY38914_Registers.h b/arm9/source/emucore/AY38914_Registers.h new file mode 100644 index 0000000..e31ebc5 --- /dev/null +++ b/arm9/source/emucore/AY38914_Registers.h @@ -0,0 +1,36 @@ + +#ifndef AY38914_REGISTERS_H +#define AY38914_REGISTERS_H + +#include "RAM.h" + +class AY38914; + +class AY38914_Registers : public RAM +{ + friend class AY38914; + + public: + void reset(); + void poke(UINT16 location, UINT16 value); + UINT16 peek(UINT16 location); + + inline size_t getMemoryByteSize() { + return sizeof(memory); + } + void getMemory(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, memory + offset, size); + } + void setMemory(void* src, UINT16 offset, UINT16 size) { + memcpy(memory + offset, src, size); + } + + private: + AY38914_Registers(UINT16 address); + void init(AY38914* ay38914); + + AY38914* ay38914; + UINT16 memory[0x0E]; +}; + +#endif diff --git a/arm9/source/emucore/AudioMixer.cpp b/arm9/source/emucore/AudioMixer.cpp new file mode 100644 index 0000000..f789460 --- /dev/null +++ b/arm9/source/emucore/AudioMixer.cpp @@ -0,0 +1,161 @@ +#include +#include +#include +#include "AudioMixer.h" +#include "AudioOutputLine.h" + +UINT16 audio_mixer_buffer[1500] __attribute__((section(".dtcm"))); + +extern UINT64 lcm(UINT64, UINT64); + +AudioMixer::AudioMixer() + : Processor("Audio Mixer"), + audioProducerCount(0), + commonClocksPerTick(0), + sampleBuffer(NULL), + sampleBufferSize(0), + sampleCount(0), + sampleSize(0) +{ + memset(&audioProducers, 0, sizeof(audioProducers)); +} + +AudioMixer::~AudioMixer() +{ + for (UINT32 i = 0; i < audioProducerCount; i++) + delete audioProducers[i]->audioOutputLine; +} + +void AudioMixer::addAudioProducer(AudioProducer* p) +{ + audioProducers[audioProducerCount] = p; + audioProducers[audioProducerCount]->audioOutputLine = + new AudioOutputLine(); + audioProducerCount++; +} + +void AudioMixer::removeAudioProducer(AudioProducer* p) +{ + for (UINT32 i = 0; i < audioProducerCount; i++) { + if (audioProducers[i] == p) { + delete p->audioOutputLine; + for (UINT32 j = i; j < (audioProducerCount-1); j++) + audioProducers[j] = audioProducers[j+1]; + audioProducerCount--; + return; + } + } +} + +void AudioMixer::removeAll() +{ + while (audioProducerCount) + removeAudioProducer(audioProducers[0]); +} + +void AudioMixer::resetProcessor() +{ + //reset instance data + commonClocksPerTick = 0; + sampleCount = 0; + + if (sampleBuffer) { + memset(sampleBuffer, 0, sampleBufferSize); + } + + //iterate through my audio output lines to determine the common output clock + UINT64 totalClockSpeed = getClockSpeed(); + for (UINT32 i = 0; i < audioProducerCount; i++) { + audioProducers[i]->audioOutputLine->reset(); + totalClockSpeed = lcm(totalClockSpeed, ((UINT64)audioProducers[i]->getClockSpeed())); + } + + //iterate again to determine the clock factor of each + commonClocksPerTick = totalClockSpeed / getClockSpeed(); + for (UINT32 i = 0; i < audioProducerCount; i++) { + audioProducers[i]->audioOutputLine->commonClocksPerSample = (totalClockSpeed / audioProducers[i]->getClockSpeed()) + * audioProducers[i]->getClocksPerSample(); + } +} + +void AudioMixer::init(UINT32 sampleRate) +{ + // TODO: assert if sampleRate/clockSpeed is 0 + + AudioMixer::release(); + + clockSpeed = sampleRate; + sampleSize = ( clockSpeed / 60.0 ); + sampleBufferSize = sampleSize * sizeof(INT16); + sampleBuffer = (INT16*) audio_mixer_buffer; + + if (sampleBuffer) { + memset(sampleBuffer, 0, sampleBufferSize); + } +} + +void AudioMixer::release() +{ + if (sampleBuffer) { + sampleBufferSize = 0; + sampleSize = 0; + sampleCount = 0; + } +} + +INT32 AudioMixer::getClockSpeed() +{ + return clockSpeed; +} + +ITCM_CODE INT32 AudioMixer::tick(INT32 minimum) +{ + // TODO: assert if sampleCount >= sampleSize + + for (int totalTicks = 0; totalTicks < minimum; totalTicks++) { + //mix and flush the sample buffers + INT64 totalSample = 0; + for (UINT32 i = 0; i < audioProducerCount; i++) { + AudioOutputLine* nextLine = audioProducers[i]->audioOutputLine; + + INT64 missingClocks = (this->commonClocksPerTick - nextLine->commonClockCounter); + INT64 sampleToUse = (missingClocks < 0 ? nextLine->previousSample : nextLine->currentSample); + + //account for when audio producers idle by adding enough samples to each producer's buffer + //to fill the time since last sample calculation + INT64 missingSampleCount = (missingClocks / nextLine->commonClocksPerSample); + if (missingSampleCount != 0) { + nextLine->sampleBuffer += missingSampleCount * sampleToUse * nextLine->commonClocksPerSample; + nextLine->commonClockCounter += missingSampleCount * nextLine->commonClocksPerSample; + missingClocks -= missingSampleCount * nextLine->commonClocksPerSample; + } + INT64 partialSample = sampleToUse * missingClocks; + + //calculate the sample for this line + totalSample += (INT16)((nextLine->sampleBuffer + partialSample) / commonClocksPerTick); + + //clear the sample buffer for this line + nextLine->sampleBuffer = -partialSample; + nextLine->commonClockCounter = -missingClocks; + } + + if (audioProducerCount > 1) { + totalSample = totalSample / audioProducerCount; + } + + sampleBuffer[sampleCount++] = totalSample; + + if (sampleCount == sampleSize) { + flushAudio(); + } + } + + return minimum; +} + +void AudioMixer::flushAudio() +{ + //the platform subclass must copy the sampleBuffer to the device + //before calling here (which discards the contents of sampleBuffer) + sampleCount = 0; +} diff --git a/arm9/source/emucore/AudioMixer.h b/arm9/source/emucore/AudioMixer.h new file mode 100644 index 0000000..e413829 --- /dev/null +++ b/arm9/source/emucore/AudioMixer.h @@ -0,0 +1,54 @@ + +#ifndef AUDIOMIXER_H +#define AUDIOMIXER_H + +#include "AudioProducer.h" +#include "types.h" +#include "Processor.h" + +#define MAX_AUDIO_PRODUCERS 10 + +template class EmulatorTmpl; + +class AudioMixer : public Processor +{ + + friend class ProcessorBus; + friend class AudioOutputLine; + + public: + AudioMixer(); + virtual ~AudioMixer(); + + inline INT16 clipSample(INT64 sample) { + return sample > 32767 ? 32767 : sample < -32768 ? -32768 : (INT16)sample; + } + + virtual void resetProcessor(); + INT32 getClockSpeed(); + INT32 tick(INT32 minimum); + virtual void flushAudio(); + + //only to be called by the Emulator + virtual void init(UINT32 sampleRate); + virtual void release(); + + void addAudioProducer(AudioProducer*); + void removeAudioProducer(AudioProducer*); + void removeAll(); + + protected: + //output info + INT32 clockSpeed; + + AudioProducer* audioProducers[MAX_AUDIO_PRODUCERS]; + UINT32 audioProducerCount; + + INT64 commonClocksPerTick; + INT16* sampleBuffer; + UINT32 sampleBufferSize; + UINT32 sampleCount; + UINT32 sampleSize; +}; + +#endif diff --git a/arm9/source/emucore/AudioOutputLine.cpp b/arm9/source/emucore/AudioOutputLine.cpp new file mode 100644 index 0000000..4a193a1 --- /dev/null +++ b/arm9/source/emucore/AudioOutputLine.cpp @@ -0,0 +1,31 @@ + +#include "AudioOutputLine.h" +#include "AudioMixer.h" + +AudioOutputLine::AudioOutputLine() + : sampleBuffer(0), + previousSample(0), + currentSample(0), + commonClockCounter(0), + commonClocksPerSample(0) +{} + +void AudioOutputLine::reset() +{ + sampleBuffer = 0; + previousSample = 0; + currentSample = 0; + commonClockCounter = 0; + commonClocksPerSample = 0; +} + +void AudioOutputLine::playSample(INT16 sample) +{ + sampleBuffer += currentSample * commonClocksPerSample; + commonClockCounter += commonClocksPerSample; + previousSample = currentSample; + currentSample = sample; +} + + + diff --git a/arm9/source/emucore/AudioOutputLine.h b/arm9/source/emucore/AudioOutputLine.h new file mode 100644 index 0000000..ac25298 --- /dev/null +++ b/arm9/source/emucore/AudioOutputLine.h @@ -0,0 +1,29 @@ + +#ifndef AUDIOOUTPUTLINE_H +#define AUDIOOUTPUTLINE_H + +#include "types.h" + +class AudioOutputLine +{ + + friend class AudioMixer; + + public: + void playSample(INT16 sample); + + private: + AudioOutputLine(); + void reset(); + + INT64 sampleBuffer; + INT64 previousSample; + INT64 currentSample; + INT64 commonClockCounter; + INT64 commonClocksPerSample; + +}; + +#endif + + diff --git a/arm9/source/emucore/AudioProducer.h b/arm9/source/emucore/AudioProducer.h new file mode 100644 index 0000000..613f519 --- /dev/null +++ b/arm9/source/emucore/AudioProducer.h @@ -0,0 +1,26 @@ + +#ifndef AUDIOPRODUCER_H +#define AUDIOPRODUCER_H + +#include "AudioOutputLine.h" + +/** + * This interface is implemented by any piece of hardware that produces audio. + */ +class AudioProducer +{ + + friend class AudioMixer; + +public: + AudioProducer() : audioOutputLine(NULL) {} + + virtual INT32 getClockSpeed() = 0; + virtual INT32 getClocksPerSample() = 0; + + protected: + AudioOutputLine* audioOutputLine; + +}; + +#endif diff --git a/arm9/source/emucore/BackTabRAM.cpp b/arm9/source/emucore/BackTabRAM.cpp new file mode 100644 index 0000000..01b8e1f --- /dev/null +++ b/arm9/source/emucore/BackTabRAM.cpp @@ -0,0 +1,79 @@ + +#include "BackTabRAM.h" + +BackTabRAM::BackTabRAM() +: RAM(BACKTAB_SIZE, BACKTAB_LOCATION, 0xFFFF, 0xFFFF) +{} + +void BackTabRAM::reset() +{ + dirtyRAM = TRUE; + colorAdvanceBitsDirty = TRUE; + for (UINT16 i = 0; i < BACKTAB_SIZE; i++) { + image[i] = 0; + dirtyBytes[i] = TRUE; + } +} + +UINT16 BackTabRAM::peek(UINT16 location) +{ + return image[location-BACKTAB_LOCATION]; +} + +void BackTabRAM::poke(UINT16 location, UINT16 value) +{ + value &= 0xFFFF; + location -= BACKTAB_LOCATION; + + if (image[location] == value) + return; + + if ((image[location] & 0x2000) != (value & 0x2000)) + colorAdvanceBitsDirty = TRUE; + + image[location] = value; + dirtyBytes[location] = TRUE; + dirtyRAM = TRUE; +} + +void BackTabRAM::markClean() { + if (!dirtyRAM) + return; + + for (UINT16 i = 0; i < BACKTAB_SIZE; i++) + dirtyBytes[i] = FALSE; + dirtyRAM = FALSE; + colorAdvanceBitsDirty = FALSE; +} + +BOOL BackTabRAM::areColorAdvanceBitsDirty() { + return colorAdvanceBitsDirty; +} + +BOOL BackTabRAM::isDirty() { + return dirtyRAM; +} + +BOOL BackTabRAM::isDirty(UINT16 location) { + return dirtyBytes[location-BACKTAB_LOCATION]; +} + +BackTabRAMState BackTabRAM::getState() +{ + BackTabRAMState state = {0}; + + state.RAMState = RAM::getState(state.image); + this->getImage(state.image, 0, this->getImageByteSize()); + + return state; +} + +void BackTabRAM::setState(BackTabRAMState state, UINT16* image) +{ + memset(this->dirtyBytes, TRUE, sizeof(this->dirtyBytes)); + RAM::setState(state.RAMState, NULL); + this->setImage(state.image, 0, this->getImageByteSize()); + + this->dirtyRAM = TRUE; + this->colorAdvanceBitsDirty = TRUE; +} diff --git a/arm9/source/emucore/BackTabRAM.h b/arm9/source/emucore/BackTabRAM.h new file mode 100644 index 0000000..f1ebeba --- /dev/null +++ b/arm9/source/emucore/BackTabRAM.h @@ -0,0 +1,52 @@ + +#ifndef BACKTABRAM_H +#define BACKTABRAM_H + +#include "RAM.h" + +#define BACKTAB_SIZE 0xF0 +#define BACKTAB_LOCATION 0x200 + +TYPEDEF_STRUCT_PACK(_BackTabRAMState +{ + _RAMState RAMState; + UINT16 image[BACKTAB_SIZE]; +} BackTabRAMState;) + +class BackTabRAM : public RAM +{ + + public: + BackTabRAM(); + void reset(); + + UINT16 peek(UINT16 location); + void poke(UINT16 location, UINT16 value); + + BOOL areColorAdvanceBitsDirty(); + BOOL isDirty(); + BOOL isDirty(UINT16 location); + void markClean(); + + inline size_t getImageByteSize() { + return size * sizeof(UINT16); + } + void getImage(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, image + offset, size); + } + void setImage(void* src, UINT16 offset, UINT16 size) { + memcpy(image + offset, src, size); + } + + BackTabRAMState getState(); + void setState(BackTabRAMState state, UINT16* image); + + private: + UINT16 image[BACKTAB_SIZE]; + BOOL dirtyBytes[BACKTAB_SIZE]; + BOOL dirtyRAM; + BOOL colorAdvanceBitsDirty; + +}; + +#endif diff --git a/arm9/source/emucore/CP1610.cpp b/arm9/source/emucore/CP1610.cpp new file mode 100644 index 0000000..cf0f4ef --- /dev/null +++ b/arm9/source/emucore/CP1610.cpp @@ -0,0 +1,4258 @@ +#include +#include +#include "CP1610.h" +#include "types.h" + +#define MAX(v1, v2) (v1 > v2 ? v1 : v2) + +//the eight registers available in the CP1610 +UINT16 r[8] __attribute__((section(".dtcm"))); + +//the six flags available in the CP1610 +UINT8 S __attribute__((section(".dtcm"))); +UINT8 Z __attribute__((section(".dtcm"))); +UINT8 O __attribute__((section(".dtcm"))); +UINT8 C __attribute__((section(".dtcm"))); +UINT8 I __attribute__((section(".dtcm"))); +UINT8 D __attribute__((section(".dtcm"))); + +UINT16 op __attribute__((section(".dtcm"))); + +//indicates whether the last executed instruction is interruptible +UINT8 interruptible __attribute__((section(".dtcm"))); + +//the four external lines +INT8 ext __attribute__((section(".dtcm")));; + +#pragma warning(disable:4786) // Suppress STL debug info > 255 chars messages + +CP1610::CP1610(MemoryBus* m, UINT16 resetAddress, + UINT16 interruptAddress) + : Processor("CP1610"), + memoryBus(m), + resetAddress(resetAddress), + interruptAddress(interruptAddress) +{ +} + +INT32 CP1610::getClockSpeed() { + return 3579545; +} + +void CP1610::resetProcessor() +{ + //the four external lines + ext = 0; + + pinOut[CP1610_PIN_OUT_BUSAK]->isHigh = TRUE; + interruptible = FALSE; + S = C = O = Z = I = D = FALSE; + for (INT32 i = 0; i < 7; i++) + r[i] = 0; + r[7] = resetAddress; +} + +/** + * This method ticks the CPU and returns the number of cycles that were + * used up, indicating to the main emulation loop when the CPU will need + * to be ticked again. + */ +ITCM_CODE INT32 CP1610::tick(INT32 minimum) +{ + int usedCycles = 0; + + // --------------------------------------------------------------------- + // Small speedup so we don't have to shift usedCycles on every loop.. + // --------------------------------------------------------------------- + int min_shifted = (minimum >> 2); + if ((min_shifted<<2) != minimum) min_shifted++; + + do { + if (interruptible) + { + if (!pinIn[CP1610_PIN_IN_BUSRQ]->isHigh) + { + pinOut[CP1610_PIN_OUT_BUSAK]->isHigh = pinIn[CP1610_PIN_IN_BUSRQ]->isHigh; + return MAX((usedCycles<<2), minimum); + } + + if (I && !pinIn[CP1610_PIN_IN_INTRM]->isHigh) + { + pinIn[CP1610_PIN_IN_INTRM]->isHigh = TRUE; + interruptible = false; + memoryBus->poke(r[6], r[7]); + r[6]++; + r[7] = interruptAddress; + usedCycles += 7; + if ((usedCycles << 2) >= minimum) + return (usedCycles<<2); + } + } + + //do the next instruction + op = memoryBus->peek_pc(); + usedCycles += decode(); + } while ((usedCycles) < min_shifted); + + return (usedCycles<<2); +} + + +UINT16 CP1610::getIndirect(UINT16 registerNum) +{ + UINT16 value; + if (registerNum == 6) + { + r[6]--; + value = memoryBus->peek_fast(r[6]); + if (D) + value = (value & 0xFF) | ((memoryBus->peek(r[6]) & 0xFF) << 8); + } + else + { + value = memoryBus->peek(r[registerNum]); + if (registerNum & 0x04) + r[registerNum]++; + if (D) { + value = (value & 0xFF) | + (memoryBus->peek(r[registerNum]) & 0xFF) << 8; + if (registerNum & 0x04) + r[registerNum]++; + } + } + + return value; +} + +INT32 CP1610::HLT() { + return 1; +} + +INT32 CP1610::SDBD() { + r[7]++; + interruptible = FALSE; + + D = TRUE; + + return 4; +} + +INT32 CP1610::EIS() { + r[7]++; + interruptible = FALSE; + + I = TRUE; + + D = FALSE; + return 4; +} + +INT32 CP1610::DIS() { + r[7]++; + interruptible = FALSE; + + I = FALSE; + + D = FALSE; + return 4; +} + +INT32 CP1610::TCI() { + r[7]++; + interruptible = FALSE; + + //What should this really do? + + D = FALSE; + return 4; +} + +INT32 CP1610::CLRC() { + r[7]++; + interruptible = FALSE; + + C = FALSE; + + D = FALSE; + return 4; +} + +INT32 CP1610::SETC() { + r[7]++; + interruptible = FALSE; + + C = TRUE; + + D = FALSE; + return 4; +} + +INT32 CP1610::J(UINT16 target) { + r[7] = target; + interruptible = TRUE; + + D = FALSE; + return 12; +} + +INT32 CP1610::JSR(UINT16 registerNum, UINT16 target) { + r[registerNum] = r[7]+3; + r[7] = target; + interruptible = TRUE; + + D = FALSE; + return 12; +} + +INT32 CP1610::JE(UINT16 target) { + I = TRUE; + r[7] = target; + interruptible = TRUE; + + D = FALSE; + return 12; +} + +INT32 CP1610::JSRE(UINT16 registerNum, UINT16 target) { + I = TRUE; + r[registerNum] = r[7]+3; + r[7] = target; + interruptible = TRUE; + + D = FALSE; + return 12; +} + +INT32 CP1610::JD(UINT16 target) { + I = FALSE; + r[7] = target; + interruptible = TRUE; + + D = FALSE; + return 12; +} + +INT32 CP1610::JSRD(UINT16 registerNum, UINT16 target) { + I = FALSE; + r[registerNum] = r[7]+3; + r[7] = target; + interruptible = TRUE; + + D = FALSE; + return 12; +} + +INT32 CP1610::INCR(UINT16 registerNum) { + r[7]++; + interruptible = TRUE; + + UINT16 newValue = r[registerNum]+1; + S = !!(newValue & 0x8000); + Z = !newValue; + r[registerNum] = newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::DECR(UINT16 registerNum) { + r[7]++; + interruptible = TRUE; + + UINT16 newValue = r[registerNum]-1; + S = !!(newValue & 0x8000); + Z = !newValue; + r[registerNum] = newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::NEGR(UINT16 registerNum) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = r[registerNum]; + UINT32 newValue = (op1 ^ 0xFFFF) + 1; + C = !!(newValue & 0x10000); + O = !!(newValue & op1 & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[registerNum] = (UINT16)newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::ADCR(UINT16 registerNum) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = r[registerNum]; + UINT16 op2 = (C ? 1 : 0); + UINT32 newValue = op1 + op2; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & ~(op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[registerNum] = (UINT16)newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::RSWD(UINT16 registerNum) { + r[7]++; + interruptible = TRUE; + + UINT16 value = r[registerNum]; + S = !!(value & 0x0080); + Z = !!(value & 0x0040); + O = !!(value & 0x0020); + C = !!(value & 0x0010); + + D = FALSE; + return 6; +} + +INT32 CP1610::GSWD(UINT16 registerNum) { + r[7]++; + interruptible = TRUE; + + UINT16 value = ((S ? 1 : 0) << 7) | ((Z ? 1 : 0) << 6) | + ((O ? 1 : 0) << 5) | ((C ? 1 : 0) << 4); + value |= (value << 8); + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::NOP(UINT16) { + r[7]++; + interruptible = TRUE; + + D = FALSE; + return 6; +} + +INT32 CP1610::SIN(UINT16) { + r[7]++; + interruptible = TRUE; + + //TODO: does SIN need to do anything?! + + D = FALSE; + return 6; +} + +INT32 CP1610::SWAP_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + value = ((value & 0xFF00) >> 8) | ((value & 0xFF) << 8); + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::SWAP_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum] & 0xFF; + value |= (value << 8); + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::COMR(UINT16 registerNum) { + r[7]++; + interruptible = TRUE; + + UINT16 value = r[registerNum] ^ 0xFFFF; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::SLL_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum] << 1; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::SLL_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum] << 2; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::RLC_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + UINT16 carry = (C ? 1 : 0); + C = !!(value & 0x8000); + value = (value << 1) | carry; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::RLC_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + UINT16 carry = (C ? 1 : 0); + UINT16 overflow = (O ? 1 : 0); + C = !!(value & 0x8000); + O = !!(value & 0x4000); + value = (value << 2) | (carry << 1) | overflow; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::SLLC_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + C = !!(value & 0x8000); + value <<= 1; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::SLLC_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + C = !!(value & 0x8000); + O = !!(value & 0x4000); + value <<= 2; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::SLR_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum] >> 1; + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::SLR_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum] >> 2; + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::SAR_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + value = (value >> 1) | (value & 0x8000); + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::SAR_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + UINT16 s = value & 0x8000; + value = (value >> 2) | s | (s >> 1); + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::RRC_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + UINT16 carry = (C ? 1 : 0); + C = !!(value & 0x0001); + value = (value >> 1) | (carry << 15); + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::RRC_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + UINT16 carry = (C ? 1 : 0); + UINT16 overflow = (O ? 1 : 0); + C = !!(value & 0x0001); + O = !!(value & 0x0002); + value = (value >> 2) | (carry << 14) | (overflow << 15); + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::SARC_1(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + C = !!(value & 0x0001); + value = (value >> 1) | (value & 0x8000); + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 6; +} + +INT32 CP1610::SARC_2(UINT16 registerNum) { + r[7]++; + interruptible = FALSE; + + UINT16 value = r[registerNum]; + C = !!(value & 0x0001); + O = !!(value & 0x0002); + UINT16 s = value & 0x8000; + value = (value >> 2) | s | (s >> 1); + S = !!(value & 0x0080); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 8; +} + +INT32 CP1610::MOVR(UINT16 sourceReg, UINT16 destReg) { + r[7]++; + interruptible = TRUE; + + UINT16 value = r[sourceReg]; + S = !!(value & 0x8000); + Z = !value; + r[destReg] = value; + + D = FALSE; + return (destReg >= 6 ? 7 : 6); +} + +INT32 CP1610::ADDR(UINT16 sourceReg, UINT16 destReg) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = r[sourceReg]; + UINT16 op2 = r[destReg]; + UINT32 newValue = op1 + op2; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & ~(op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[destReg] = (UINT16)newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::SUBR(UINT16 sourceReg, UINT16 destReg) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = r[sourceReg]; + UINT16 op2 = r[destReg]; + UINT32 newValue = op2 + (0xFFFF ^ op1) + 1; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & (op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[destReg] = (UINT16)newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::CMPR(UINT16 sourceReg, UINT16 destReg) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = r[sourceReg]; + UINT16 op2 = r[destReg]; + UINT32 newValue = op2 + (0xFFFF ^ op1) + 1; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & (op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + + D = FALSE; + return 6; +} + +INT32 CP1610::ANDR(UINT16 sourceReg, UINT16 destReg) { + r[7]++; + interruptible = TRUE; + + UINT16 newValue = r[destReg] & r[sourceReg]; + S = !!(newValue & 0x8000); + Z = !newValue; + r[destReg] = newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::XORR(UINT16 sourceReg, UINT16 destReg) { + r[7]++; + interruptible = TRUE; + + UINT16 newValue = r[destReg] ^ r[sourceReg]; + S = !!(newValue & 0x8000); + Z = !newValue; + r[destReg] = newValue; + + D = FALSE; + return 6; +} + +INT32 CP1610::BEXT(UINT16 condition, INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (ext == condition) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::B(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + r[7] = (UINT16)(r[7] + displacement); + + D = FALSE; + return 9; +} + +INT32 CP1610::NOPP(INT16) { + r[7] += 2; + interruptible = TRUE; + + D = FALSE; + return 7; +} + +INT32 CP1610::BC(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (C) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BNC(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (!C) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BOV(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (O) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BNOV(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (!O) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BPL(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (!S) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BMI(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (S) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BEQ(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (Z) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BNEQ(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (!Z) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BLT(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (S != O) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BGE(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (S == O) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BLE(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (Z || (S != O)) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BGT(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (!(Z || (S != O))) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BUSC(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (C != S) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::BESC(INT16 displacement) { + r[7] += 2; + interruptible = TRUE; + + if (C == S) { + r[7] = (UINT16)(r[7] + displacement); + D = FALSE; + return 9; + } + + D = FALSE; + return 7; +} + +INT32 CP1610::MVO(UINT16 registerNum, UINT16 address) { + r[7] += 2; + interruptible = FALSE; + + memoryBus->poke(address, r[registerNum]); + + D = FALSE; + return 11; +} + +INT32 CP1610::MVO_ind(UINT16 registerWithAddress, UINT16 registerToMove) { + r[7]++; + interruptible = FALSE; + + memoryBus->poke(r[registerWithAddress], r[registerToMove]); + + //if the register number is 4-7, increment it + if (registerWithAddress & 0x04) + r[registerWithAddress]++; + + D = FALSE; + return 9; +} + +INT32 CP1610::MVI(UINT16 address, UINT16 registerNum) { + r[7] += 2; + interruptible = TRUE; + + r[registerNum] = memoryBus->peek(address); + + D = FALSE; + return 10; +} + +INT32 CP1610::MVI_ind(UINT16 registerWithAddress, UINT16 registerToReceive) { + r[7]++; + interruptible = TRUE; + + r[registerToReceive] = getIndirect(registerWithAddress); + + D = FALSE; + return (D ? 10 : (registerWithAddress == 6 ? 11 : 8)); +} + +INT32 CP1610::ADD(UINT16 address, UINT16 registerNum) { + r[7] += 2; + interruptible = TRUE; + + UINT16 op1 = memoryBus->peek(address); + UINT16 op2 = r[registerNum]; + UINT32 newValue = op1 + op2; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & ~(op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[registerNum] = (UINT16)newValue; + + D = FALSE; + return 10; +} + +INT32 CP1610::ADD_ind(UINT16 registerWithAddress, UINT16 registerToReceive) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = getIndirect(registerWithAddress); + UINT16 op2 = r[registerToReceive]; + UINT32 newValue = op1 + op2; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & ~(op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[registerToReceive] = (UINT16)newValue; + + D = FALSE; + return (D ? 10 : (registerWithAddress == 6 ? 11 : 8)); +} + +INT32 CP1610::SUB(UINT16 address, UINT16 registerNum) { + r[7] += 2; + interruptible = TRUE; + + UINT16 op1 = memoryBus->peek(address); + UINT16 op2 = r[registerNum]; + UINT32 newValue = op2 + (0xFFFF ^ op1) + 1; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & (op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[registerNum] = (UINT16)newValue; + + D = FALSE; + return 10; +} + +INT32 CP1610::SUB_ind(UINT16 registerWithAddress, UINT16 registerToReceive) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = getIndirect(registerWithAddress); + UINT16 op2 = r[registerToReceive]; + UINT32 newValue = op2 + (0xFFFF ^ op1) + 1; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & (op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + r[registerToReceive] = (UINT16)newValue; + + D = FALSE; + return (D ? 10 : (registerWithAddress == 6 ? 11 : 8)); +} + +INT32 CP1610::CMP(UINT16 address, UINT16 registerNum) { + r[7] += 2; + interruptible = TRUE; + + UINT16 op1 = memoryBus->peek(address); + UINT16 op2 = r[registerNum]; + UINT32 newValue = op2 + (0xFFFF ^ op1) + 1; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & (op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + + D = FALSE; + return 10; +} + +INT32 CP1610::CMP_ind(UINT16 registerWithAddress, UINT16 registerToReceive) { + r[7]++; + interruptible = TRUE; + + UINT16 op1 = getIndirect(registerWithAddress); + UINT16 op2 = r[registerToReceive]; + UINT32 newValue = op2 + (0xFFFF ^ op1) + 1; + C = !!(newValue & 0x10000); + O = !!((op2 ^ newValue) & (op1 ^ op2) & 0x8000); + S = !!(newValue & 0x8000); + Z = !(newValue & 0xFFFF); + + D = FALSE; + return (D ? 10 : (registerWithAddress == 6 ? 11 : 8)); +} + +INT32 CP1610::AND(UINT16 address, UINT16 registerNum) { + r[7] += 2; + interruptible = TRUE; + + UINT16 value = memoryBus->peek(address) & r[registerNum]; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 10; +} + +INT32 CP1610::AND_ind(UINT16 registerWithAddress, UINT16 registerToReceive) { + r[7]++; + interruptible = TRUE; + + UINT16 value = getIndirect(registerWithAddress) & r[registerToReceive]; + S = !!(value & 0x8000); + Z = !value; + r[registerToReceive] = value; + + D = FALSE; + return (D ? 10 : (registerWithAddress == 6 ? 11 : 8)); +} + +INT32 CP1610::XOR(UINT16 address, UINT16 registerNum) { + r[7] += 2; + interruptible = TRUE; + + UINT16 value = memoryBus->peek(address) ^ r[registerNum]; + S = !!(value & 0x8000); + Z = !value; + r[registerNum] = value; + + D = FALSE; + return 10; +} + +INT32 CP1610::XOR_ind(UINT16 registerWithAddress, UINT16 registerToReceive) { + r[7]++; + interruptible = TRUE; + + UINT16 value = getIndirect(registerWithAddress) ^ r[registerToReceive]; + S = !!(value & 0x8000); + Z = !value; + r[registerToReceive] = value; + + D = FALSE; + return (D ? 10 : (registerWithAddress == 6 ? 11 : 8)); +} + +INT32 CP1610::decode(void) +{ + switch (op & 0x3FF) { + case 0x0000: + return HLT(); + case 0x0001: + return SDBD(); + case 0x0002: + return EIS(); + case 0x0003: + return DIS(); + case 0x0004: + { + int read = memoryBus->peek((UINT16)(r[7] + 1)); + int reg = ((read & 0x0300) >> 8); + int interrupt = (read & 0x0003); + UINT16 target = (UINT16)((read & 0x00FC) << 8); + read = memoryBus->peek((UINT16)(r[7] + 2)); + target |= (UINT16)(read & 0x03FF); + if (reg == 3) { + if (interrupt == 0) + return J(target); + else if (interrupt == 1) + return JE(target); + else if (interrupt == 2) + return JD(target); + else + return HLT(); //invalid opcode + } + else { + if (interrupt == 0) + return JSR((UINT16)(reg + 4), target); + else if (interrupt == 1) + return JSRE((UINT16)(reg + 4), target); + else if (interrupt == 2) + return JSRD((UINT16)(reg + 4), target); + else + return HLT(); //invalid opcode + } + } + case 0x0005: + return TCI(); + case 0x0006: + return CLRC(); + case 0x0007: + return SETC(); + case 0x0008: + return INCR(0); + case 0x0009: + return INCR(1); + case 0x000A: + return INCR(2); + case 0x000B: + return INCR(3); + case 0x000C: + return INCR(4); + case 0x000D: + return INCR(5); + case 0x000E: + return INCR(6); + case 0x000F: + return INCR(7); + case 0x0010: + return DECR(0); + + case 0x0011: + return DECR(1); + + case 0x0012: + return DECR(2); + + case 0x0013: + return DECR(3); + + case 0x0014: + return DECR(4); + + case 0x0015: + return DECR(5); + + case 0x0016: + return DECR(6); + + case 0x0017: + return DECR(7); + + case 0x0018: + return COMR(0); + + case 0x0019: + return COMR(1); + + case 0x001A: + return COMR(2); + + case 0x001B: + return COMR(3); + + case 0x001C: + return COMR(4); + + case 0x001D: + return COMR(5); + + case 0x001E: + return COMR(6); + + case 0x001F: + return COMR(7); + + case 0x0020: + return NEGR(0); + + case 0x0021: + return NEGR(1); + + case 0x0022: + return NEGR(2); + + case 0x0023: + return NEGR(3); + + case 0x0024: + return NEGR(4); + + case 0x0025: + return NEGR(5); + + case 0x0026: + return NEGR(6); + + case 0x0027: + return NEGR(7); + + case 0x0028: + return ADCR(0); + + case 0x0029: + return ADCR(1); + + case 0x002A: + return ADCR(2); + + case 0x002B: + return ADCR(3); + + case 0x002C: + return ADCR(4); + + case 0x002D: + return ADCR(5); + + case 0x002E: + return ADCR(6); + + case 0x002F: + return ADCR(7); + + case 0x0030: + return GSWD(0); + + case 0x0031: + return GSWD(1); + + case 0x0032: + return GSWD(2); + + case 0x0033: + return GSWD(3); + + case 0x0034: + return NOP(0); + + case 0x0035: + return NOP(1); + + case 0x0036: + return SIN(0); + + case 0x0037: + return SIN(1); + + case 0x0038: + return RSWD(0); + + case 0x0039: + return RSWD(1); + + case 0x003A: + return RSWD(2); + + case 0x003B: + return RSWD(3); + + case 0x003C: + return RSWD(4); + + case 0x003D: + return RSWD(5); + + case 0x003E: + return RSWD(6); + + case 0x003F: + return RSWD(7); + + case 0x0040: + return SWAP_1(0); + + case 0x0041: + return SWAP_1(1); + + case 0x0042: + return SWAP_1(2); + + case 0x0043: + return SWAP_1(3); + + case 0x0044: + return SWAP_2(0); + + case 0x0045: + return SWAP_2(1); + + case 0x0046: + return SWAP_2(2); + + case 0x0047: + return SWAP_2(3); + + case 0x0048: + return SLL_1(0); + + case 0x0049: + return SLL_1(1); + + case 0x004A: + return SLL_1(2); + + case 0x004B: + return SLL_1(3); + + case 0x004C: + return SLL_2(0); + + case 0x004D: + return SLL_2(1); + + case 0x004E: + return SLL_2(2); + + case 0x004F: + return SLL_2(3); + + case 0x0050: + return RLC_1(0); + + case 0x0051: + return RLC_1(1); + + case 0x0052: + return RLC_1(2); + + case 0x0053: + return RLC_1(3); + + case 0x0054: + return RLC_2(0); + + case 0x0055: + return RLC_2(1); + + case 0x0056: + return RLC_2(2); + + case 0x0057: + return RLC_2(3); + + case 0x0058: + return SLLC_1(0); + + case 0x0059: + return SLLC_1(1); + + case 0x005A: + return SLLC_1(2); + + case 0x005B: + return SLLC_1(3); + + case 0x005C: + return SLLC_2(0); + + case 0x005D: + return SLLC_2(1); + + case 0x005E: + return SLLC_2(2); + + case 0x005F: + return SLLC_2(3); + + case 0x0060: + return SLR_1(0); + + case 0x0061: + return SLR_1(1); + + case 0x0062: + return SLR_1(2); + + case 0x0063: + return SLR_1(3); + + case 0x0064: + return SLR_2(0); + + case 0x0065: + return SLR_2(1); + + case 0x0066: + return SLR_2(2); + + case 0x0067: + return SLR_2(3); + + case 0x0068: + return SAR_1(0); + + case 0x0069: + return SAR_1(1); + + case 0x006A: + return SAR_1(2); + + case 0x006B: + return SAR_1(3); + + case 0x006C: + return SAR_2(0); + + case 0x006D: + return SAR_2(1); + + case 0x006E: + return SAR_2(2); + + case 0x006F: + return SAR_2(3); + + case 0x0070: + return RRC_1(0); + + case 0x0071: + return RRC_1(1); + + case 0x0072: + return RRC_1(2); + + case 0x0073: + return RRC_1(3); + + case 0x0074: + return RRC_2(0); + + case 0x0075: + return RRC_2(1); + + case 0x0076: + return RRC_2(2); + + case 0x0077: + return RRC_2(3); + + case 0x0078: + return SARC_1(0); + + case 0x0079: + return SARC_1(1); + + case 0x007A: + return SARC_1(2); + + case 0x007B: + return SARC_1(3); + + case 0x007C: + return SARC_2(0); + + case 0x007D: + return SARC_2(1); + + case 0x007E: + return SARC_2(2); + + case 0x007F: + return SARC_2(3); + + case 0x0080: + return MOVR(0, 0); + + case 0x0081: + return MOVR(0, 1); + + case 0x0082: + return MOVR(0, 2); + + case 0x0083: + return MOVR(0, 3); + + case 0x0084: + return MOVR(0, 4); + + case 0x0085: + return MOVR(0, 5); + + case 0x0086: + return MOVR(0, 6); + + case 0x0087: + return MOVR(0, 7); + + case 0x0088: + return MOVR(1, 0); + + case 0x0089: + return MOVR(1, 1); + + case 0x008A: + return MOVR(1, 2); + + case 0x008B: + return MOVR(1, 3); + + case 0x008C: + return MOVR(1, 4); + + case 0x008D: + return MOVR(1, 5); + + case 0x008E: + return MOVR(1, 6); + + case 0x008F: + return MOVR(1, 7); + + case 0x0090: + return MOVR(2, 0); + + case 0x0091: + return MOVR(2, 1); + + case 0x0092: + return MOVR(2, 2); + + case 0x0093: + return MOVR(2, 3); + + case 0x0094: + return MOVR(2, 4); + + case 0x0095: + return MOVR(2, 5); + + case 0x0096: + return MOVR(2, 6); + + case 0x0097: + return MOVR(2, 7); + + case 0x0098: + return MOVR(3, 0); + + case 0x0099: + return MOVR(3, 1); + + case 0x009A: + return MOVR(3, 2); + + case 0x009B: + return MOVR(3, 3); + + case 0x009C: + return MOVR(3, 4); + + case 0x009D: + return MOVR(3, 5); + + case 0x009E: + return MOVR(3, 6); + + case 0x009F: + return MOVR(3, 7); + + case 0x00A0: + return MOVR(4, 0); + + case 0x00A1: + return MOVR(4, 1); + + case 0x00A2: + return MOVR(4, 2); + + case 0x00A3: + return MOVR(4, 3); + + case 0x00A4: + return MOVR(4, 4); + + case 0x00A5: + return MOVR(4, 5); + + case 0x00A6: + return MOVR(4, 6); + + case 0x00A7: + return MOVR(4, 7); + + case 0x00A8: + return MOVR(5, 0); + + case 0x00A9: + return MOVR(5, 1); + + case 0x00AA: + return MOVR(5, 2); + + case 0x00AB: + return MOVR(5, 3); + + case 0x00AC: + return MOVR(5, 4); + + case 0x00AD: + return MOVR(5, 5); + + case 0x00AE: + return MOVR(5, 6); + + case 0x00AF: + return MOVR(5, 7); + + case 0x00B0: + return MOVR(6, 0); + + case 0x00B1: + return MOVR(6, 1); + + case 0x00B2: + return MOVR(6, 2); + + case 0x00B3: + return MOVR(6, 3); + + case 0x00B4: + return MOVR(6, 4); + + case 0x00B5: + return MOVR(6, 5); + + case 0x00B6: + return MOVR(6, 6); + + case 0x00B7: + return MOVR(6, 7); + + case 0x00B8: + return MOVR(7, 0); + + case 0x00B9: + return MOVR(7, 1); + + case 0x00BA: + return MOVR(7, 2); + + case 0x00BB: + return MOVR(7, 3); + + case 0x00BC: + return MOVR(7, 4); + + case 0x00BD: + return MOVR(7, 5); + + case 0x00BE: + return MOVR(7, 6); + + case 0x00BF: + return MOVR(7, 7); + + case 0x00C0: + return ADDR(0, 0); + + case 0x00C1: + return ADDR(0, 1); + + case 0x00C2: + return ADDR(0, 2); + + case 0x00C3: + return ADDR(0, 3); + + case 0x00C4: + return ADDR(0, 4); + + case 0x00C5: + return ADDR(0, 5); + + case 0x00C6: + return ADDR(0, 6); + + case 0x00C7: + return ADDR(0, 7); + + case 0x00C8: + return ADDR(1, 0); + + case 0x00C9: + return ADDR(1, 1); + + case 0x00CA: + return ADDR(1, 2); + + case 0x00CB: + return ADDR(1, 3); + + case 0x00CC: + return ADDR(1, 4); + + case 0x00CD: + return ADDR(1, 5); + + case 0x00CE: + return ADDR(1, 6); + + case 0x00CF: + return ADDR(1, 7); + + case 0x00D0: + return ADDR(2, 0); + + case 0x00D1: + return ADDR(2, 1); + + case 0x00D2: + return ADDR(2, 2); + + case 0x00D3: + return ADDR(2, 3); + + case 0x00D4: + return ADDR(2, 4); + + case 0x00D5: + return ADDR(2, 5); + + case 0x00D6: + return ADDR(2, 6); + + case 0x00D7: + return ADDR(2, 7); + + case 0x00D8: + return ADDR(3, 0); + + case 0x00D9: + return ADDR(3, 1); + + case 0x00DA: + return ADDR(3, 2); + + case 0x00DB: + return ADDR(3, 3); + + case 0x00DC: + return ADDR(3, 4); + + case 0x00DD: + return ADDR(3, 5); + + case 0x00DE: + return ADDR(3, 6); + + case 0x00DF: + return ADDR(3, 7); + + case 0x00E0: + return ADDR(4, 0); + + case 0x00E1: + return ADDR(4, 1); + + case 0x00E2: + return ADDR(4, 2); + + case 0x00E3: + return ADDR(4, 3); + + case 0x00E4: + return ADDR(4, 4); + + case 0x00E5: + return ADDR(4, 5); + + case 0x00E6: + return ADDR(4, 6); + + case 0x00E7: + return ADDR(4, 7); + + case 0x00E8: + return ADDR(5, 0); + + case 0x00E9: + return ADDR(5, 1); + + case 0x00EA: + return ADDR(5, 2); + + case 0x00EB: + return ADDR(5, 3); + + case 0x00EC: + return ADDR(5, 4); + + case 0x00ED: + return ADDR(5, 5); + + case 0x00EE: + return ADDR(5, 6); + + case 0x00EF: + return ADDR(5, 7); + + case 0x00F0: + return ADDR(6, 0); + + case 0x00F1: + return ADDR(6, 1); + + case 0x00F2: + return ADDR(6, 2); + + case 0x00F3: + return ADDR(6, 3); + + case 0x00F4: + return ADDR(6, 4); + + case 0x00F5: + return ADDR(6, 5); + + case 0x00F6: + return ADDR(6, 6); + + case 0x00F7: + return ADDR(6, 7); + + case 0x00F8: + return ADDR(7, 0); + + case 0x00F9: + return ADDR(7, 1); + + case 0x00FA: + return ADDR(7, 2); + + case 0x00FB: + return ADDR(7, 3); + + case 0x00FC: + return ADDR(7, 4); + + case 0x00FD: + return ADDR(7, 5); + + case 0x00FE: + return ADDR(7, 6); + + case 0x00FF: + return ADDR(7, 7); + + case 0x0100: + return SUBR(0, 0); + + case 0x0101: + return SUBR(0, 1); + + case 0x0102: + return SUBR(0, 2); + + case 0x0103: + return SUBR(0, 3); + + case 0x0104: + return SUBR(0, 4); + + case 0x0105: + return SUBR(0, 5); + + case 0x0106: + return SUBR(0, 6); + + case 0x0107: + return SUBR(0, 7); + + case 0x0108: + return SUBR(1, 0); + + case 0x0109: + return SUBR(1, 1); + + case 0x010A: + return SUBR(1, 2); + + case 0x010B: + return SUBR(1, 3); + + case 0x010C: + return SUBR(1, 4); + + case 0x010D: + return SUBR(1, 5); + + case 0x010E: + return SUBR(1, 6); + + case 0x010F: + return SUBR(1, 7); + + case 0x0110: + return SUBR(2, 0); + + case 0x0111: + return SUBR(2, 1); + + case 0x0112: + return SUBR(2, 2); + + case 0x0113: + return SUBR(2, 3); + + case 0x0114: + return SUBR(2, 4); + + case 0x0115: + return SUBR(2, 5); + + case 0x0116: + return SUBR(2, 6); + + case 0x0117: + return SUBR(2, 7); + + case 0x0118: + return SUBR(3, 0); + + case 0x0119: + return SUBR(3, 1); + + case 0x011A: + return SUBR(3, 2); + + case 0x011B: + return SUBR(3, 3); + + case 0x011C: + return SUBR(3, 4); + + case 0x011D: + return SUBR(3, 5); + + case 0x011E: + return SUBR(3, 6); + + case 0x011F: + return SUBR(3, 7); + + case 0x0120: + return SUBR(4, 0); + + case 0x0121: + return SUBR(4, 1); + + case 0x0122: + return SUBR(4, 2); + + case 0x0123: + return SUBR(4, 3); + + case 0x0124: + return SUBR(4, 4); + + case 0x0125: + return SUBR(4, 5); + + case 0x0126: + return SUBR(4, 6); + + case 0x0127: + return SUBR(4, 7); + + case 0x0128: + return SUBR(5, 0); + + case 0x0129: + return SUBR(5, 1); + + case 0x012A: + return SUBR(5, 2); + + case 0x012B: + return SUBR(5, 3); + + case 0x012C: + return SUBR(5, 4); + + case 0x012D: + return SUBR(5, 5); + + case 0x012E: + return SUBR(5, 6); + + case 0x012F: + return SUBR(5, 7); + + case 0x0130: + return SUBR(6, 0); + + case 0x0131: + return SUBR(6, 1); + + case 0x0132: + return SUBR(6, 2); + + case 0x0133: + return SUBR(6, 3); + + case 0x0134: + return SUBR(6, 4); + + case 0x0135: + return SUBR(6, 5); + + case 0x0136: + return SUBR(6, 6); + + case 0x0137: + return SUBR(6, 7); + + case 0x0138: + return SUBR(7, 0); + + case 0x0139: + return SUBR(7, 1); + + case 0x013A: + return SUBR(7, 2); + + case 0x013B: + return SUBR(7, 3); + + case 0x013C: + return SUBR(7, 4); + + case 0x013D: + return SUBR(7, 5); + + case 0x013E: + return SUBR(7, 6); + + case 0x013F: + return SUBR(7, 7); + + case 0x0140: + return CMPR(0, 0); + + case 0x0141: + return CMPR(0, 1); + + case 0x0142: + return CMPR(0, 2); + + case 0x0143: + return CMPR(0, 3); + + case 0x0144: + return CMPR(0, 4); + + case 0x0145: + return CMPR(0, 5); + + case 0x0146: + return CMPR(0, 6); + + case 0x0147: + return CMPR(0, 7); + + case 0x0148: + return CMPR(1, 0); + + case 0x0149: + return CMPR(1, 1); + + case 0x014A: + return CMPR(1, 2); + + case 0x014B: + return CMPR(1, 3); + + case 0x014C: + return CMPR(1, 4); + + case 0x014D: + return CMPR(1, 5); + + case 0x014E: + return CMPR(1, 6); + + case 0x014F: + return CMPR(1, 7); + + case 0x0150: + return CMPR(2, 0); + + case 0x0151: + return CMPR(2, 1); + + case 0x0152: + return CMPR(2, 2); + + case 0x0153: + return CMPR(2, 3); + + case 0x0154: + return CMPR(2, 4); + + case 0x0155: + return CMPR(2, 5); + + case 0x0156: + return CMPR(2, 6); + + case 0x0157: + return CMPR(2, 7); + + case 0x0158: + return CMPR(3, 0); + + case 0x0159: + return CMPR(3, 1); + + case 0x015A: + return CMPR(3, 2); + + case 0x015B: + return CMPR(3, 3); + + case 0x015C: + return CMPR(3, 4); + + case 0x015D: + return CMPR(3, 5); + + case 0x015E: + return CMPR(3, 6); + + case 0x015F: + return CMPR(3, 7); + + case 0x0160: + return CMPR(4, 0); + + case 0x0161: + return CMPR(4, 1); + + case 0x0162: + return CMPR(4, 2); + + case 0x0163: + return CMPR(4, 3); + + case 0x0164: + return CMPR(4, 4); + + case 0x0165: + return CMPR(4, 5); + + case 0x0166: + return CMPR(4, 6); + + case 0x0167: + return CMPR(4, 7); + + case 0x0168: + return CMPR(5, 0); + + case 0x0169: + return CMPR(5, 1); + + case 0x016A: + return CMPR(5, 2); + + case 0x016B: + return CMPR(5, 3); + + case 0x016C: + return CMPR(5, 4); + + case 0x016D: + return CMPR(5, 5); + + case 0x016E: + return CMPR(5, 6); + + case 0x016F: + return CMPR(5, 7); + + case 0x0170: + return CMPR(6, 0); + + case 0x0171: + return CMPR(6, 1); + + case 0x0172: + return CMPR(6, 2); + + case 0x0173: + return CMPR(6, 3); + + case 0x0174: + return CMPR(6, 4); + + case 0x0175: + return CMPR(6, 5); + + case 0x0176: + return CMPR(6, 6); + + case 0x0177: + return CMPR(6, 7); + + case 0x0178: + return CMPR(7, 0); + + case 0x0179: + return CMPR(7, 1); + + case 0x017A: + return CMPR(7, 2); + + case 0x017B: + return CMPR(7, 3); + + case 0x017C: + return CMPR(7, 4); + + case 0x017D: + return CMPR(7, 5); + + case 0x017E: + return CMPR(7, 6); + + case 0x017F: + return CMPR(7, 7); + + case 0x0180: + return ANDR(0, 0); + + case 0x0181: + return ANDR(0, 1); + + case 0x0182: + return ANDR(0, 2); + + case 0x0183: + return ANDR(0, 3); + + case 0x0184: + return ANDR(0, 4); + + case 0x0185: + return ANDR(0, 5); + + case 0x0186: + return ANDR(0, 6); + + case 0x0187: + return ANDR(0, 7); + + case 0x0188: + return ANDR(1, 0); + + case 0x0189: + return ANDR(1, 1); + + case 0x018A: + return ANDR(1, 2); + + case 0x018B: + return ANDR(1, 3); + + case 0x018C: + return ANDR(1, 4); + + case 0x018D: + return ANDR(1, 5); + + case 0x018E: + return ANDR(1, 6); + + case 0x018F: + return ANDR(1, 7); + + case 0x0190: + return ANDR(2, 0); + + case 0x0191: + return ANDR(2, 1); + + case 0x0192: + return ANDR(2, 2); + + case 0x0193: + return ANDR(2, 3); + + case 0x0194: + return ANDR(2, 4); + + case 0x0195: + return ANDR(2, 5); + + case 0x0196: + return ANDR(2, 6); + + case 0x0197: + return ANDR(2, 7); + + case 0x0198: + return ANDR(3, 0); + + case 0x0199: + return ANDR(3, 1); + + case 0x019A: + return ANDR(3, 2); + + case 0x019B: + return ANDR(3, 3); + + case 0x019C: + return ANDR(3, 4); + + case 0x019D: + return ANDR(3, 5); + + case 0x019E: + return ANDR(3, 6); + + case 0x019F: + return ANDR(3, 7); + + case 0x01A0: + return ANDR(4, 0); + + case 0x01A1: + return ANDR(4, 1); + + case 0x01A2: + return ANDR(4, 2); + + case 0x01A3: + return ANDR(4, 3); + + case 0x01A4: + return ANDR(4, 4); + + case 0x01A5: + return ANDR(4, 5); + + case 0x01A6: + return ANDR(4, 6); + + case 0x01A7: + return ANDR(4, 7); + + case 0x01A8: + return ANDR(5, 0); + + case 0x01A9: + return ANDR(5, 1); + + case 0x01AA: + return ANDR(5, 2); + + case 0x01AB: + return ANDR(5, 3); + + case 0x01AC: + return ANDR(5, 4); + + case 0x01AD: + return ANDR(5, 5); + + case 0x01AE: + return ANDR(5, 6); + + case 0x01AF: + return ANDR(5, 7); + + case 0x01B0: + return ANDR(6, 0); + + case 0x01B1: + return ANDR(6, 1); + + case 0x01B2: + return ANDR(6, 2); + + case 0x01B3: + return ANDR(6, 3); + + case 0x01B4: + return ANDR(6, 4); + + case 0x01B5: + return ANDR(6, 5); + + case 0x01B6: + return ANDR(6, 6); + + case 0x01B7: + return ANDR(6, 7); + + case 0x01B8: + return ANDR(7, 0); + + case 0x01B9: + return ANDR(7, 1); + + case 0x01BA: + return ANDR(7, 2); + + case 0x01BB: + return ANDR(7, 3); + + case 0x01BC: + return ANDR(7, 4); + + case 0x01BD: + return ANDR(7, 5); + + case 0x01BE: + return ANDR(7, 6); + + case 0x01BF: + return ANDR(7, 7); + + case 0x01C0: + return XORR(0, 0); + + case 0x01C1: + return XORR(0, 1); + + case 0x01C2: + return XORR(0, 2); + + case 0x01C3: + return XORR(0, 3); + + case 0x01C4: + return XORR(0, 4); + + case 0x01C5: + return XORR(0, 5); + + case 0x01C6: + return XORR(0, 6); + + case 0x01C7: + return XORR(0, 7); + + case 0x01C8: + return XORR(1, 0); + + case 0x01C9: + return XORR(1, 1); + + case 0x01CA: + return XORR(1, 2); + + case 0x01CB: + return XORR(1, 3); + + case 0x01CC: + return XORR(1, 4); + + case 0x01CD: + return XORR(1, 5); + + case 0x01CE: + return XORR(1, 6); + + case 0x01CF: + return XORR(1, 7); + + case 0x01D0: + return XORR(2, 0); + + case 0x01D1: + return XORR(2, 1); + + case 0x01D2: + return XORR(2, 2); + + case 0x01D3: + return XORR(2, 3); + + case 0x01D4: + return XORR(2, 4); + + case 0x01D5: + return XORR(2, 5); + + case 0x01D6: + return XORR(2, 6); + + case 0x01D7: + return XORR(2, 7); + + case 0x01D8: + return XORR(3, 0); + + case 0x01D9: + return XORR(3, 1); + + case 0x01DA: + return XORR(3, 2); + + case 0x01DB: + return XORR(3, 3); + + case 0x01DC: + return XORR(3, 4); + + case 0x01DD: + return XORR(3, 5); + + case 0x01DE: + return XORR(3, 6); + + case 0x01DF: + return XORR(3, 7); + + case 0x01E0: + return XORR(4, 0); + + case 0x01E1: + return XORR(4, 1); + + case 0x01E2: + return XORR(4, 2); + + case 0x01E3: + return XORR(4, 3); + + case 0x01E4: + return XORR(4, 4); + + case 0x01E5: + return XORR(4, 5); + + case 0x01E6: + return XORR(4, 6); + + case 0x01E7: + return XORR(4, 7); + + case 0x01E8: + return XORR(5, 0); + + case 0x01E9: + return XORR(5, 1); + + case 0x01EA: + return XORR(5, 2); + + case 0x01EB: + return XORR(5, 3); + + case 0x01EC: + return XORR(5, 4); + + case 0x01ED: + return XORR(5, 5); + + case 0x01EE: + return XORR(5, 6); + + case 0x01EF: + return XORR(5, 7); + + case 0x01F0: + return XORR(6, 0); + + case 0x01F1: + return XORR(6, 1); + + case 0x01F2: + return XORR(6, 2); + + case 0x01F3: + return XORR(6, 3); + + case 0x01F4: + return XORR(6, 4); + + case 0x01F5: + return XORR(6, 5); + + case 0x01F6: + return XORR(6, 6); + + case 0x01F7: + return XORR(6, 7); + + case 0x01F8: + return XORR(7, 0); + + case 0x01F9: + return XORR(7, 1); + + case 0x01FA: + return XORR(7, 2); + + case 0x01FB: + return XORR(7, 3); + + case 0x01FC: + return XORR(7, 4); + + case 0x01FD: + return XORR(7, 5); + + case 0x01FE: + return XORR(7, 6); + + case 0x01FF: + return XORR(7, 7); + + case 0x0200: + return B(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0201: + return BC(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0202: + return BOV(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0203: + return BPL(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0204: + return BEQ(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0205: + return BLT(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0206: + return BLE(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0207: + return BUSC(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0208: + return NOPP(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0209: + return BNC(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x020A: + return BNOV(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x020B: + return BMI(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x020C: + return BNEQ(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x020D: + return BGE(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x020E: + return BGT(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x020F: + return BESC(memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0210: + return BEXT(0, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0211: + return BEXT(1, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0212: + return BEXT(2, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0213: + return BEXT(3, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0214: + return BEXT(4, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0215: + return BEXT(5, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0216: + return BEXT(6, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0217: + return BEXT(7, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0218: + return BEXT(8, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0219: + return BEXT(9, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x021A: + return BEXT(10, + memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x021B: + return BEXT(11, + memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x021C: + return BEXT(12, + memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x021D: + return BEXT(13, + memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x021E: + return BEXT(14, + memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x021F: + return BEXT(15, + memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0220: + return B(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0221: + return BC(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0222: + return BOV(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0223: + return BPL(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0224: + return BEQ(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0225: + return BLT(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0226: + return BLE(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0227: + return BUSC(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0228: + return NOPP(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0229: + return BNC(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x022A: + return BNOV(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x022B: + return BMI(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x022C: + return BNEQ(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x022D: + return BGE(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x022E: + return BGT(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x022F: + return BESC(-memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0230: + return BEXT(0, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0231: + return BEXT(1, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0232: + return BEXT(2, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0233: + return BEXT(3, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0234: + return BEXT(4, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0235: + return BEXT(5, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0236: + return BEXT(6, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0237: + return BEXT(7, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0238: + return BEXT(8, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0239: + return BEXT(9, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x023A: + return BEXT(10, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x023B: + return BEXT(11, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x023C: + return BEXT(12, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x023D: + return BEXT(13, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x023E: + return BEXT(14, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x023F: + return BEXT(15, + -memoryBus->peek((UINT16)(r[7] + 1)) - 1); + + case 0x0240: + return MVO(0, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0241: + return MVO(1, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0242: + return MVO(2, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0243: + return MVO(3, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0244: + return MVO(4, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0245: + return MVO(5, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0246: + return MVO(6, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0247: + return MVO(7, memoryBus->peek((UINT16)(r[7] + 1))); + + case 0x0248: + return MVO_ind(1, 0); + + case 0x0249: + return MVO_ind(1, 1); + + case 0x024A: + return MVO_ind(1, 2); + + case 0x024B: + return MVO_ind(1, 3); + + case 0x024C: + return MVO_ind(1, 4); + + case 0x024D: + return MVO_ind(1, 5); + + case 0x024E: + return MVO_ind(1, 6); + + case 0x024F: + return MVO_ind(1, 7); + + case 0x0250: + return MVO_ind(2, 0); + + case 0x0251: + return MVO_ind(2, 1); + + case 0x0252: + return MVO_ind(2, 2); + + case 0x0253: + return MVO_ind(2, 3); + + case 0x0254: + return MVO_ind(2, 4); + + case 0x0255: + return MVO_ind(2, 5); + + case 0x0256: + return MVO_ind(2, 6); + + case 0x0257: + return MVO_ind(2, 7); + + case 0x0258: + return MVO_ind(3, 0); + + case 0x0259: + return MVO_ind(3, 1); + + case 0x025A: + return MVO_ind(3, 2); + + case 0x025B: + return MVO_ind(3, 3); + + case 0x025C: + return MVO_ind(3, 4); + + case 0x025D: + return MVO_ind(3, 5); + + case 0x025E: + return MVO_ind(3, 6); + + case 0x025F: + return MVO_ind(3, 7); + + case 0x0260: + return MVO_ind(4, 0); + + case 0x0261: + return MVO_ind(4, 1); + + case 0x0262: + return MVO_ind(4, 2); + + case 0x0263: + return MVO_ind(4, 3); + + case 0x0264: + return MVO_ind(4, 4); + + case 0x0265: + return MVO_ind(4, 5); + + case 0x0266: + return MVO_ind(4, 6); + + case 0x0267: + return MVO_ind(4, 7); + + case 0x0268: + return MVO_ind(5, 0); + + case 0x0269: + return MVO_ind(5, 1); + + case 0x026A: + return MVO_ind(5, 2); + + case 0x026B: + return MVO_ind(5, 3); + + case 0x026C: + return MVO_ind(5, 4); + + case 0x026D: + return MVO_ind(5, 5); + + case 0x026E: + return MVO_ind(5, 6); + + case 0x026F: + return MVO_ind(5, 7); + + case 0x0270: + return MVO_ind(6, 0); + + case 0x0271: + return MVO_ind(6, 1); + + case 0x0272: + return MVO_ind(6, 2); + + case 0x0273: + return MVO_ind(6, 3); + + case 0x0274: + return MVO_ind(6, 4); + + case 0x0275: + return MVO_ind(6, 5); + + case 0x0276: + return MVO_ind(6, 6); + + case 0x0277: + return MVO_ind(6, 7); + + case 0x0278: + return MVO_ind(7, 0); + + case 0x0279: + return MVO_ind(7, 1); + + case 0x027A: + return MVO_ind(7, 2); + + case 0x027B: + return MVO_ind(7, 3); + + case 0x027C: + return MVO_ind(7, 4); + + case 0x027D: + return MVO_ind(7, 5); + + case 0x027E: + return MVO_ind(7, 6); + + case 0x027F: + return MVO_ind(7, 7); + + case 0x0280: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 0); + + case 0x0281: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 1); + + case 0x0282: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 2); + + case 0x0283: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 3); + + case 0x0284: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 4); + + case 0x0285: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 5); + + case 0x0286: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 6); + + case 0x0287: + return MVI(memoryBus->peek((UINT16)(r[7] + 1)), 7); + + case 0x0288: + return MVI_ind(1, 0); + + case 0x0289: + return MVI_ind(1, 1); + + case 0x028A: + return MVI_ind(1, 2); + + case 0x028B: + return MVI_ind(1, 3); + + case 0x028C: + return MVI_ind(1, 4); + + case 0x028D: + return MVI_ind(1, 5); + + case 0x028E: + return MVI_ind(1, 6); + + case 0x028F: + return MVI_ind(1, 7); + + case 0x0290: + return MVI_ind(2, 0); + + case 0x0291: + return MVI_ind(2, 1); + + case 0x0292: + return MVI_ind(2, 2); + + case 0x0293: + return MVI_ind(2, 3); + + case 0x0294: + return MVI_ind(2, 4); + + case 0x0295: + return MVI_ind(2, 5); + + case 0x0296: + return MVI_ind(2, 6); + + case 0x0297: + return MVI_ind(2, 7); + + case 0x0298: + return MVI_ind(3, 0); + + case 0x0299: + return MVI_ind(3, 1); + + case 0x029A: + return MVI_ind(3, 2); + + case 0x029B: + return MVI_ind(3, 3); + + case 0x029C: + return MVI_ind(3, 4); + + case 0x029D: + return MVI_ind(3, 5); + + case 0x029E: + return MVI_ind(3, 6); + + case 0x029F: + return MVI_ind(3, 7); + + case 0x02A0: + return MVI_ind(4, 0); + + case 0x02A1: + return MVI_ind(4, 1); + + case 0x02A2: + return MVI_ind(4, 2); + + case 0x02A3: + return MVI_ind(4, 3); + + case 0x02A4: + return MVI_ind(4, 4); + + case 0x02A5: + return MVI_ind(4, 5); + + case 0x02A6: + return MVI_ind(4, 6); + + case 0x02A7: + return MVI_ind(4, 7); + + case 0x02A8: + return MVI_ind(5, 0); + + case 0x02A9: + return MVI_ind(5, 1); + + case 0x02AA: + return MVI_ind(5, 2); + + case 0x02AB: + return MVI_ind(5, 3); + + case 0x02AC: + return MVI_ind(5, 4); + + case 0x02AD: + return MVI_ind(5, 5); + + case 0x02AE: + return MVI_ind(5, 6); + + case 0x02AF: + return MVI_ind(5, 7); + + case 0x02B0: + return MVI_ind(6, 0); + + case 0x02B1: + return MVI_ind(6, 1); + + case 0x02B2: + return MVI_ind(6, 2); + + case 0x02B3: + return MVI_ind(6, 3); + + case 0x02B4: + return MVI_ind(6, 4); + + case 0x02B5: + return MVI_ind(6, 5); + + case 0x02B6: + return MVI_ind(6, 6); + + case 0x02B7: + return MVI_ind(6, 7); + + case 0x02B8: + return MVI_ind(7, 0); + + case 0x02B9: + return MVI_ind(7, 1); + + case 0x02BA: + return MVI_ind(7, 2); + + case 0x02BB: + return MVI_ind(7, 3); + + case 0x02BC: + return MVI_ind(7, 4); + + case 0x02BD: + return MVI_ind(7, 5); + + case 0x02BE: + return MVI_ind(7, 6); + + case 0x02BF: + return MVI_ind(7, 7); + + case 0x02C0: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 0); + + case 0x02C1: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 1); + + case 0x02C2: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 2); + + case 0x02C3: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 3); + + case 0x02C4: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 4); + + case 0x02C5: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 5); + + case 0x02C6: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 6); + + case 0x02C7: + return ADD(memoryBus->peek((UINT16)(r[7] + 1)), 7); + + case 0x02C8: + return ADD_ind(1, 0); + + case 0x02C9: + return ADD_ind(1, 1); + + case 0x02CA: + return ADD_ind(1, 2); + + case 0x02CB: + return ADD_ind(1, 3); + + case 0x02CC: + return ADD_ind(1, 4); + + case 0x02CD: + return ADD_ind(1, 5); + + case 0x02CE: + return ADD_ind(1, 6); + + case 0x02CF: + return ADD_ind(1, 7); + + case 0x02D0: + return ADD_ind(2, 0); + + case 0x02D1: + return ADD_ind(2, 1); + + case 0x02D2: + return ADD_ind(2, 2); + + case 0x02D3: + return ADD_ind(2, 3); + + case 0x02D4: + return ADD_ind(2, 4); + + case 0x02D5: + return ADD_ind(2, 5); + + case 0x02D6: + return ADD_ind(2, 6); + + case 0x02D7: + return ADD_ind(2, 7); + + case 0x02D8: + return ADD_ind(3, 0); + + case 0x02D9: + return ADD_ind(3, 1); + + case 0x02DA: + return ADD_ind(3, 2); + + case 0x02DB: + return ADD_ind(3, 3); + + case 0x02DC: + return ADD_ind(3, 4); + + case 0x02DD: + return ADD_ind(3, 5); + + case 0x02DE: + return ADD_ind(3, 6); + + case 0x02DF: + return ADD_ind(3, 7); + + case 0x02E0: + return ADD_ind(4, 0); + + case 0x02E1: + return ADD_ind(4, 1); + + case 0x02E2: + return ADD_ind(4, 2); + + case 0x02E3: + return ADD_ind(4, 3); + + case 0x02E4: + return ADD_ind(4, 4); + + case 0x02E5: + return ADD_ind(4, 5); + + case 0x02E6: + return ADD_ind(4, 6); + + case 0x02E7: + return ADD_ind(4, 7); + + case 0x02E8: + return ADD_ind(5, 0); + + case 0x02E9: + return ADD_ind(5, 1); + + case 0x02EA: + return ADD_ind(5, 2); + + case 0x02EB: + return ADD_ind(5, 3); + + case 0x02EC: + return ADD_ind(5, 4); + + case 0x02ED: + return ADD_ind(5, 5); + + case 0x02EE: + return ADD_ind(5, 6); + + case 0x02EF: + return ADD_ind(5, 7); + + case 0x02F0: + return ADD_ind(6, 0); + + case 0x02F1: + return ADD_ind(6, 1); + + case 0x02F2: + return ADD_ind(6, 2); + + case 0x02F3: + return ADD_ind(6, 3); + + case 0x02F4: + return ADD_ind(6, 4); + + case 0x02F5: + return ADD_ind(6, 5); + + case 0x02F6: + return ADD_ind(6, 6); + + case 0x02F7: + return ADD_ind(6, 7); + + case 0x02F8: + return ADD_ind(7, 0); + + case 0x02F9: + return ADD_ind(7, 1); + + case 0x02FA: + return ADD_ind(7, 2); + + case 0x02FB: + return ADD_ind(7, 3); + + case 0x02FC: + return ADD_ind(7, 4); + + case 0x02FD: + return ADD_ind(7, 5); + + case 0x02FE: + return ADD_ind(7, 6); + + case 0x02FF: + return ADD_ind(7, 7); + + case 0x0300: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 0); + + case 0x0301: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 1); + + case 0x0302: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 2); + + case 0x0303: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 3); + + case 0x0304: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 4); + + case 0x0305: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 5); + + case 0x0306: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 6); + + case 0x0307: + return SUB(memoryBus->peek((UINT16)(r[7] + 1)), 7); + + case 0x0308: + return SUB_ind(1, 0); + + case 0x0309: + return SUB_ind(1, 1); + + case 0x030A: + return SUB_ind(1, 2); + + case 0x030B: + return SUB_ind(1, 3); + + case 0x030C: + return SUB_ind(1, 4); + + case 0x030D: + return SUB_ind(1, 5); + + case 0x030E: + return SUB_ind(1, 6); + + case 0x030F: + return SUB_ind(1, 7); + + case 0x0310: + return SUB_ind(2, 0); + + case 0x0311: + return SUB_ind(2, 1); + + case 0x0312: + return SUB_ind(2, 2); + + case 0x0313: + return SUB_ind(2, 3); + + case 0x0314: + return SUB_ind(2, 4); + + case 0x0315: + return SUB_ind(2, 5); + + case 0x0316: + return SUB_ind(2, 6); + + case 0x0317: + return SUB_ind(2, 7); + + case 0x0318: + return SUB_ind(3, 0); + + case 0x0319: + return SUB_ind(3, 1); + + case 0x031A: + return SUB_ind(3, 2); + + case 0x031B: + return SUB_ind(3, 3); + + case 0x031C: + return SUB_ind(3, 4); + + case 0x031D: + return SUB_ind(3, 5); + + case 0x031E: + return SUB_ind(3, 6); + + case 0x031F: + return SUB_ind(3, 7); + + case 0x0320: + return SUB_ind(4, 0); + + case 0x0321: + return SUB_ind(4, 1); + + case 0x0322: + return SUB_ind(4, 2); + + case 0x0323: + return SUB_ind(4, 3); + + case 0x0324: + return SUB_ind(4, 4); + + case 0x0325: + return SUB_ind(4, 5); + + case 0x0326: + return SUB_ind(4, 6); + + case 0x0327: + return SUB_ind(4, 7); + + case 0x0328: + return SUB_ind(5, 0); + + case 0x0329: + return SUB_ind(5, 1); + + case 0x032A: + return SUB_ind(5, 2); + + case 0x032B: + return SUB_ind(5, 3); + + case 0x032C: + return SUB_ind(5, 4); + + case 0x032D: + return SUB_ind(5, 5); + + case 0x032E: + return SUB_ind(5, 6); + + case 0x032F: + return SUB_ind(5, 7); + + case 0x0330: + return SUB_ind(6, 0); + + case 0x0331: + return SUB_ind(6, 1); + + case 0x0332: + return SUB_ind(6, 2); + + case 0x0333: + return SUB_ind(6, 3); + + case 0x0334: + return SUB_ind(6, 4); + + case 0x0335: + return SUB_ind(6, 5); + + case 0x0336: + return SUB_ind(6, 6); + + case 0x0337: + return SUB_ind(6, 7); + + case 0x0338: + return SUB_ind(7, 0); + + case 0x0339: + return SUB_ind(7, 1); + + case 0x033A: + return SUB_ind(7, 2); + + case 0x033B: + return SUB_ind(7, 3); + + case 0x033C: + return SUB_ind(7, 4); + + case 0x033D: + return SUB_ind(7, 5); + + case 0x033E: + return SUB_ind(7, 6); + + case 0x033F: + return SUB_ind(7, 7); + + case 0x0340: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 0); + + case 0x0341: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 1); + + case 0x0342: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 2); + + case 0x0343: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 3); + + case 0x0344: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 4); + + case 0x0345: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 5); + + case 0x0346: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 6); + + case 0x0347: + return CMP(memoryBus->peek((UINT16)(r[7] + 1)), 7); + + case 0x0348: + return CMP_ind(1, 0); + + case 0x0349: + return CMP_ind(1, 1); + + case 0x034A: + return CMP_ind(1, 2); + + case 0x034B: + return CMP_ind(1, 3); + + case 0x034C: + return CMP_ind(1, 4); + + case 0x034D: + return CMP_ind(1, 5); + + case 0x034E: + return CMP_ind(1, 6); + + case 0x034F: + return CMP_ind(1, 7); + + case 0x0350: + return CMP_ind(2, 0); + + case 0x0351: + return CMP_ind(2, 1); + + case 0x0352: + return CMP_ind(2, 2); + + case 0x0353: + return CMP_ind(2, 3); + + case 0x0354: + return CMP_ind(2, 4); + + case 0x0355: + return CMP_ind(2, 5); + + case 0x0356: + return CMP_ind(2, 6); + + case 0x0357: + return CMP_ind(2, 7); + + case 0x0358: + return CMP_ind(3, 0); + + case 0x0359: + return CMP_ind(3, 1); + + case 0x035A: + return CMP_ind(3, 2); + + case 0x035B: + return CMP_ind(3, 3); + + case 0x035C: + return CMP_ind(3, 4); + + case 0x035D: + return CMP_ind(3, 5); + + case 0x035E: + return CMP_ind(3, 6); + + case 0x035F: + return CMP_ind(3, 7); + + case 0x0360: + return CMP_ind(4, 0); + + case 0x0361: + return CMP_ind(4, 1); + + case 0x0362: + return CMP_ind(4, 2); + + case 0x0363: + return CMP_ind(4, 3); + + case 0x0364: + return CMP_ind(4, 4); + + case 0x0365: + return CMP_ind(4, 5); + + case 0x0366: + return CMP_ind(4, 6); + + case 0x0367: + return CMP_ind(4, 7); + + case 0x0368: + return CMP_ind(5, 0); + + case 0x0369: + return CMP_ind(5, 1); + + case 0x036A: + return CMP_ind(5, 2); + + case 0x036B: + return CMP_ind(5, 3); + + case 0x036C: + return CMP_ind(5, 4); + + case 0x036D: + return CMP_ind(5, 5); + + case 0x036E: + return CMP_ind(5, 6); + + case 0x036F: + return CMP_ind(5, 7); + + case 0x0370: + return CMP_ind(6, 0); + + case 0x0371: + return CMP_ind(6, 1); + + case 0x0372: + return CMP_ind(6, 2); + + case 0x0373: + return CMP_ind(6, 3); + + case 0x0374: + return CMP_ind(6, 4); + + case 0x0375: + return CMP_ind(6, 5); + + case 0x0376: + return CMP_ind(6, 6); + + case 0x0377: + return CMP_ind(6, 7); + + case 0x0378: + return CMP_ind(7, 0); + + case 0x0379: + return CMP_ind(7, 1); + + case 0x037A: + return CMP_ind(7, 2); + + case 0x037B: + return CMP_ind(7, 3); + + case 0x037C: + return CMP_ind(7, 4); + + case 0x037D: + return CMP_ind(7, 5); + + case 0x037E: + return CMP_ind(7, 6); + + case 0x037F: + return CMP_ind(7, 7); + + case 0x0380: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 0); + + case 0x0381: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 1); + + case 0x0382: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 2); + + case 0x0383: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 3); + + case 0x0384: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 4); + + case 0x0385: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 5); + + case 0x0386: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 6); + + case 0x0387: + return AND(memoryBus->peek((UINT16)(r[7] + 1)), 7); + + case 0x0388: + return AND_ind(1, 0); + + case 0x0389: + return AND_ind(1, 1); + + case 0x038A: + return AND_ind(1, 2); + + case 0x038B: + return AND_ind(1, 3); + + case 0x038C: + return AND_ind(1, 4); + + case 0x038D: + return AND_ind(1, 5); + + case 0x038E: + return AND_ind(1, 6); + + case 0x038F: + return AND_ind(1, 7); + + case 0x0390: + return AND_ind(2, 0); + + case 0x0391: + return AND_ind(2, 1); + + case 0x0392: + return AND_ind(2, 2); + + case 0x0393: + return AND_ind(2, 3); + + case 0x0394: + return AND_ind(2, 4); + + case 0x0395: + return AND_ind(2, 5); + + case 0x0396: + return AND_ind(2, 6); + + case 0x0397: + return AND_ind(2, 7); + + case 0x0398: + return AND_ind(3, 0); + + case 0x0399: + return AND_ind(3, 1); + + case 0x039A: + return AND_ind(3, 2); + + case 0x039B: + return AND_ind(3, 3); + + case 0x039C: + return AND_ind(3, 4); + + case 0x039D: + return AND_ind(3, 5); + + case 0x039E: + return AND_ind(3, 6); + + case 0x039F: + return AND_ind(3, 7); + + case 0x03A0: + return AND_ind(4, 0); + + case 0x03A1: + return AND_ind(4, 1); + + case 0x03A2: + return AND_ind(4, 2); + + case 0x03A3: + return AND_ind(4, 3); + + case 0x03A4: + return AND_ind(4, 4); + + case 0x03A5: + return AND_ind(4, 5); + + case 0x03A6: + return AND_ind(4, 6); + + case 0x03A7: + return AND_ind(4, 7); + + case 0x03A8: + return AND_ind(5, 0); + + case 0x03A9: + return AND_ind(5, 1); + + case 0x03AA: + return AND_ind(5, 2); + + case 0x03AB: + return AND_ind(5, 3); + + case 0x03AC: + return AND_ind(5, 4); + + case 0x03AD: + return AND_ind(5, 5); + + case 0x03AE: + return AND_ind(5, 6); + + case 0x03AF: + return AND_ind(5, 7); + + case 0x03B0: + return AND_ind(6, 0); + + case 0x03B1: + return AND_ind(6, 1); + + case 0x03B2: + return AND_ind(6, 2); + + case 0x03B3: + return AND_ind(6, 3); + + case 0x03B4: + return AND_ind(6, 4); + + case 0x03B5: + return AND_ind(6, 5); + + case 0x03B6: + return AND_ind(6, 6); + + case 0x03B7: + return AND_ind(6, 7); + + case 0x03B8: + return AND_ind(7, 0); + + case 0x03B9: + return AND_ind(7, 1); + + case 0x03BA: + return AND_ind(7, 2); + + case 0x03BB: + return AND_ind(7, 3); + + case 0x03BC: + return AND_ind(7, 4); + + case 0x03BD: + return AND_ind(7, 5); + + case 0x03BE: + return AND_ind(7, 6); + + case 0x03BF: + return AND_ind(7, 7); + + case 0x03C0: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 0); + + case 0x03C1: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 1); + + case 0x03C2: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 2); + + case 0x03C3: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 3); + + case 0x03C4: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 4); + + case 0x03C5: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 5); + + case 0x03C6: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 6); + + case 0x03C7: + return XOR(memoryBus->peek((UINT16)(r[7] + 1)), 7); + + case 0x03C8: + return XOR_ind(1, 0); + + case 0x03C9: + return XOR_ind(1, 1); + + case 0x03CA: + return XOR_ind(1, 2); + + case 0x03CB: + return XOR_ind(1, 3); + + case 0x03CC: + return XOR_ind(1, 4); + + case 0x03CD: + return XOR_ind(1, 5); + + case 0x03CE: + return XOR_ind(1, 6); + + case 0x03CF: + return XOR_ind(1, 7); + + case 0x03D0: + return XOR_ind(2, 0); + + case 0x03D1: + return XOR_ind(2, 1); + + case 0x03D2: + return XOR_ind(2, 2); + + case 0x03D3: + return XOR_ind(2, 3); + + case 0x03D4: + return XOR_ind(2, 4); + + case 0x03D5: + return XOR_ind(2, 5); + + case 0x03D6: + return XOR_ind(2, 6); + + case 0x03D7: + return XOR_ind(2, 7); + + case 0x03D8: + return XOR_ind(3, 0); + + case 0x03D9: + return XOR_ind(3, 1); + + case 0x03DA: + return XOR_ind(3, 2); + + case 0x03DB: + return XOR_ind(3, 3); + + case 0x03DC: + return XOR_ind(3, 4); + + case 0x03DD: + return XOR_ind(3, 5); + + case 0x03DE: + return XOR_ind(3, 6); + + case 0x03DF: + return XOR_ind(3, 7); + + case 0x03E0: + return XOR_ind(4, 0); + + case 0x03E1: + return XOR_ind(4, 1); + + case 0x03E2: + return XOR_ind(4, 2); + + case 0x03E3: + return XOR_ind(4, 3); + + case 0x03E4: + return XOR_ind(4, 4); + + case 0x03E5: + return XOR_ind(4, 5); + + case 0x03E6: + return XOR_ind(4, 6); + + case 0x03E7: + return XOR_ind(4, 7); + + case 0x03E8: + return XOR_ind(5, 0); + + case 0x03E9: + return XOR_ind(5, 1); + + case 0x03EA: + return XOR_ind(5, 2); + + case 0x03EB: + return XOR_ind(5, 3); + + case 0x03EC: + return XOR_ind(5, 4); + + case 0x03ED: + return XOR_ind(5, 5); + + case 0x03EE: + return XOR_ind(5, 6); + + case 0x03EF: + return XOR_ind(5, 7); + + case 0x03F0: + return XOR_ind(6, 0); + + case 0x03F1: + return XOR_ind(6, 1); + + case 0x03F2: + return XOR_ind(6, 2); + + case 0x03F3: + return XOR_ind(6, 3); + + case 0x03F4: + return XOR_ind(6, 4); + + case 0x03F5: + return XOR_ind(6, 5); + + case 0x03F6: + return XOR_ind(6, 6); + + case 0x03F7: + return XOR_ind(6, 7); + + case 0x03F8: + return XOR_ind(7, 0); + + case 0x03F9: + return XOR_ind(7, 1); + + case 0x03FA: + return XOR_ind(7, 2); + + case 0x03FB: + return XOR_ind(7, 3); + + case 0x03FC: + return XOR_ind(7, 4); + + case 0x03FD: + return XOR_ind(7, 5); + + case 0x03FE: + return XOR_ind(7, 6); + + case 0x03FF: + default : + return XOR_ind(7, 7); + + } +} + +CP1610State CP1610::getState() +{ + CP1610State state = {0}; + state.interruptAddress = this->interruptAddress; + state.resetAddress = this->resetAddress; + memcpy(state.r, r, sizeof(r)); + state.S = S; + state.Z = Z; + state.O = O; + state.C = C; + state.I = I; + state.D = D; + state.interruptible = interruptible; + state.ext = ext; + + return state; +} + +void CP1610::setState(CP1610State state) +{ + this->interruptAddress = state.interruptAddress; + this->resetAddress = state.resetAddress; + memcpy(r, state.r, sizeof(r)); + S = state.S; + Z = state.Z; + O = state.O; + C = state.C; + I = state.I; + D = state.D; + interruptible = state.interruptible; + ext = state.ext; +} diff --git a/arm9/source/emucore/CP1610.h b/arm9/source/emucore/CP1610.h new file mode 100644 index 0000000..383f585 --- /dev/null +++ b/arm9/source/emucore/CP1610.h @@ -0,0 +1,159 @@ + +#ifndef CP1610_H +#define CP1610_H + +#include "Processor.h" +#include "SignalLine.h" +#include "MemoryBus.h" + +#define CP1610_PIN_IN_INTRM 0 +#define CP1610_PIN_IN_BUSRQ 1 + +#define CP1610_PIN_OUT_BUSAK 0 + +TYPEDEF_STRUCT_PACK( _CP1610State +{ + INT8 S; + INT8 Z; + INT8 O; + INT8 C; + INT8 I; + INT8 D; + INT8 interruptible; + INT8 ext; + UINT16 interruptAddress; + UINT16 resetAddress; + UINT16 r[8]; +} CP1610State; ) + +extern UINT8 interruptible; + +class CP1610 : public Processor +{ + + public: + CP1610(MemoryBus* m, UINT16 resetAddress, + UINT16 interruptAddress); + + //PowerConsumer functions + void resetProcessor(); + + //Processor functions + INT32 getClockSpeed(); + INT32 tick(INT32); + + BOOL isIdle() { + if (!pinIn[CP1610_PIN_IN_BUSRQ]->isHigh && interruptible) { + pinOut[CP1610_PIN_OUT_BUSAK]->isHigh = FALSE; + return TRUE; + } + else { + pinOut[CP1610_PIN_OUT_BUSAK]->isHigh = TRUE; + return FALSE; + } + } + +#ifdef DEVELOPER_VERSION + UINT32 getDebugItemCount(); + const CHAR* getDebugItemName(UINT32 i); + const UINT32 getDebugItemValue(UINT32 i); + + //other debugging stuff + BOOL isCentralProcessor() { return TRUE; } + UINT32 decode(CHAR description[256], UINT32 memoryLocation); + UINT32 getProgramCounter(); +#endif + CP1610State getState(); + void setState(CP1610State state); + + private: + void setIndirect(UINT16 register, UINT16 value); + UINT16 getIndirect(UINT16 register); + INT32 HLT(); + INT32 SDBD(); + INT32 EIS(); + INT32 DIS(); + INT32 TCI(); + INT32 CLRC(); + INT32 SETC(); + INT32 J(UINT16 target); + INT32 JSR(UINT16 register, UINT16 target); + INT32 JE(UINT16 target); + INT32 JSRE(UINT16 register, UINT16 target); + INT32 JD(UINT16 target); + INT32 JSRD(UINT16 register, UINT16 target); + INT32 INCR(UINT16 register); + INT32 DECR(UINT16 register); + INT32 COMR(UINT16 register); + INT32 NEGR(UINT16 register); + INT32 ADCR(UINT16 register); + INT32 RSWD(UINT16 register); + INT32 GSWD(UINT16 register); + INT32 NOP(UINT16 twoOption); + INT32 SIN(UINT16 twoOption); + INT32 SWAP_1(UINT16 register); + INT32 SWAP_2(UINT16 register); + INT32 SLL_1(UINT16 register); + INT32 SLL_2(UINT16 register); + INT32 RLC_1(UINT16 register); + INT32 RLC_2(UINT16 register); + INT32 SLLC_1(UINT16 register); + INT32 SLLC_2(UINT16 register); + INT32 SLR_1(UINT16 register); + INT32 SLR_2(UINT16 register); + INT32 SAR_1(UINT16 register); + INT32 SAR_2(UINT16 register); + INT32 RRC_1(UINT16 register); + INT32 RRC_2(UINT16 register); + INT32 SARC_1(UINT16 register); + INT32 SARC_2(UINT16 register); + INT32 MOVR(UINT16 sourceReg, UINT16 destReg); + INT32 ADDR(UINT16 sourceReg, UINT16 destReg); + INT32 SUBR(UINT16 sourceReg, UINT16 destReg); + INT32 CMPR(UINT16 sourceReg, UINT16 destReg); + INT32 ANDR(UINT16 sourceReg, UINT16 destReg); + INT32 XORR(UINT16 sourceReg, UINT16 destReg); + INT32 BEXT(UINT16 condition, INT16 displacement); + INT32 B(INT16 displacement); + INT32 NOPP(INT16 displacement); + INT32 BC(INT16 displacement); + INT32 BNC(INT16 displacement); + INT32 BOV(INT16 displacement); + INT32 BNOV(INT16 displacement); + INT32 BPL(INT16 displacement); + INT32 BMI(INT16 displacement); + INT32 BEQ(INT16 displacement); + INT32 BNEQ(INT16 displacement); + INT32 BLT(INT16 displacement); + INT32 BGE(INT16 displacement); + INT32 BLE(INT16 displacement); + INT32 BGT(INT16 displacement); + INT32 BUSC(INT16 displacement); + INT32 BESC(INT16 displacement); + INT32 MVO(UINT16 register, UINT16 address); + INT32 MVO_ind(UINT16 registerWithAddress, UINT16 registerToMove); + INT32 MVI(UINT16 address, UINT16 register); + INT32 MVI_ind(UINT16 registerWithAddress, UINT16 registerToReceive); + INT32 ADD(UINT16 address, UINT16 register); + INT32 ADD_ind(UINT16 registerWithAddress, UINT16 registerToReceive); + INT32 SUB(UINT16 address, UINT16 register); + INT32 SUB_ind(UINT16 registerWithAddress, UINT16 registerToReceive); + INT32 CMP(UINT16 address, UINT16 register); + INT32 CMP_ind(UINT16 registerWithAddress, UINT16 registerToReceive); + INT32 AND(UINT16 address, UINT16 register); + INT32 AND_ind(UINT16 registerWithAddress, UINT16 registerToReceive); + INT32 XOR(UINT16 address, UINT16 register); + INT32 XOR_ind(UINT16 registerWithAddress, UINT16 registerToReceive); + INT32 decode(void); + + //the mory bus + MemoryBus* memoryBus; + + //interrupt address + UINT16 interruptAddress; + + //reset address + UINT16 resetAddress; +}; + +#endif diff --git a/arm9/source/emucore/CRC32.cpp b/arm9/source/emucore/CRC32.cpp new file mode 100644 index 0000000..e76f173 --- /dev/null +++ b/arm9/source/emucore/CRC32.cpp @@ -0,0 +1,102 @@ + +#include +#include +#include "CRC32.h" + +#define CRC32_POLY 0x04C11DB7 + +//using namespace std; + +#ifndef _MAX_PATH +#define _MAX_PATH 255 +#endif + +UINT64 reflect(UINT64 ref, char ch) +{ + + UINT64 value(0); + + // Swap bit 0 for bit 7 + // bit 1 for bit 6, etc. + for(int i = 1; i < (ch + 1); i++) + { + if(ref & 1) + value |= 1 << (ch - i); + ref >>= 1; + } + return value; +} + + + +CRC32::CRC32() { + UINT32 i, j; + for (i = 0; i < 256; ++i) { + crc32_table[i]=(UINT32)(reflect(i, 8) << 24); + for (j = 0; j < 8; j++) + crc32_table[i] = (crc32_table[i] << 1) ^ + (crc32_table[i] & (1 << 31) ? CRC32_POLY : 0); + crc32_table[i] = (UINT32)reflect(crc32_table[i], 32); + } + reset(); +} + +UINT32 CRC32::getCrc(const CHAR* filename) +{ + CRC32 crc; + + FILE* file = fopen(filename, "rb"); + int read = fgetc(file); + while (read != EOF) { + crc.update((UINT8)read); + read = fgetc(file); + } + fclose(file); + + return crc.getValue(); +} + +UINT32 CRC32::getCrc(UINT8* image, UINT32 imageSize) +{ + CRC32 crc; + for (UINT32 i = 0; i < imageSize; i++) + crc.update(image[i]); + + return crc.getValue(); +} + +/* +void CRC32::formInternalFilename(CHAR* out, const CHAR* in, const CHAR* ext) +{ + strcpy(out, in); + char *p = strstr(out, ".zip"); + *p = '\0'; + strcat(out, ext); + p = &out[strlen((char *)out)]; + while(--p > out) + { + if(*p == '\\' || *p == '/') + break; + } + strcpy(out, p+1); +} +*/ + +void CRC32::reset() { + crc = 0xFFFFFFFF; +} + +void CRC32::update(UINT8 data) { + crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ data]; +} + +void CRC32::update(UINT8* data, UINT32 length) { + for (UINT32 i = 0; i < length; i++) + //crc = (crc << 8) ^ crc32_table[(crc >> 24) ^ data[i]]; + crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ data[i]]; +} + +UINT32 CRC32::getValue() { + return ~crc; +} + diff --git a/arm9/source/emucore/CRC32.h b/arm9/source/emucore/CRC32.h new file mode 100644 index 0000000..c59bdaf --- /dev/null +++ b/arm9/source/emucore/CRC32.h @@ -0,0 +1,28 @@ + +#ifndef CRC32_H +#define CRC32_H + +#include "types.h" + +class CRC32 +{ + + public: + CRC32(); + void reset(); + void update(UINT8 data); + void update(UINT8* data, UINT32 length); + UINT32 getValue(); + static UINT32 getCrc(const CHAR* filename); + static UINT32 getCrc(UINT8* image, UINT32 imageSize); + + private: + //static void formInternalFilename(CHAR*, const CHAR*, const CHAR*); + + UINT32 crc; + UINT32 crc32_table[256]; + +}; + +#endif + diff --git a/arm9/source/emucore/ECS.cpp b/arm9/source/emucore/ECS.cpp new file mode 100644 index 0000000..0ecf7fe --- /dev/null +++ b/arm9/source/emucore/ECS.cpp @@ -0,0 +1,51 @@ + +#include +#include "ECS.h" + +ECS::ECS() +: Peripheral("Electronic Computer System", "ECS"), + keyboard(2), + ramBank(ECS_RAM_SIZE, 0x4000, 8), + uart(4, 0xE0, 8), + psg2(0x00F0, &keyboard, &keyboard), + bank0("ECS ROM #1", "ecs.bin", 0, 2, 0x1000, 0x2000), + bank0Banker(&bank0, 0x2FFF, 0xFFF0, 0x2A50, 0x000F, 1), + bank1("ECS ROM #2", "ecs.bin", 8192, 2, 0x1000, 0x7000), + bank1Banker(&bank1, 0x7FFF, 0xFFF0, 0x7A50, 0x000F, 0), + bank2("ECS ROM #3", "ecs.bin", 16384, 2, 0x1000, 0xE000), + bank2Banker(&bank2, 0xEFFF, 0xFFF0, 0xEA50, 0x000F, 1) +{ + AddROM(&bank0); + AddRAM(&bank0Banker); + AddROM(&bank1); + AddRAM(&bank1Banker); + AddROM(&bank2); + AddRAM(&bank2Banker); + + AddRAM(&ramBank); + AddRAM(&uart); + + AddProcessor(&psg2); + AddAudioProducer(&psg2); + AddRAM(&psg2.registers); + + AddInputConsumer(&keyboard); +} + +ECSState ECS::getState() +{ + ECSState state = {0}; + + state.ramState = ramBank.getState(state.ramImage); + state.uartState = uart.getState(NULL); + state.psg2State = psg2.getState(); + + return state; +} + +void ECS::setState(ECSState state) +{ + ramBank.setState(state.ramState, state.ramImage); + uart.setState(state.uartState, NULL); + psg2.setState(state.psg2State); +} diff --git a/arm9/source/emucore/ECS.h b/arm9/source/emucore/ECS.h new file mode 100644 index 0000000..0b42e20 --- /dev/null +++ b/arm9/source/emucore/ECS.h @@ -0,0 +1,53 @@ + +#ifndef ECS_H +#define ECS_H + +#include "Peripheral.h" +#include "ECSKeyboard.h" +#include "AudioOutputLine.h" +#include "RAM.h" +#include "ROM.h" +#include "ROMBanker.h" +#include "types.h" +#include "AY38914.h" + +#define ECS_RAM_SIZE 0x0800 + +TYPEDEF_STRUCT_PACK( _ECSState +{ + RAMState ramState; + UINT16 ramImage[ECS_RAM_SIZE]; + RAMState uartState; + AY38914State psg2State; +} ECSState; ) + +class Intellivision; + +class ECS : public Peripheral +{ + + friend class Intellivision; + + public: + ECS(); + + ECSState getState(); + void setState(ECSState state); + + private: + ECSKeyboard keyboard; + + public: + ROM bank0; + ROMBanker bank0Banker; + ROM bank1; + ROMBanker bank1Banker; + ROM bank2; + ROMBanker bank2Banker; + RAM ramBank; + RAM uart; + AY38914 psg2; + +}; + +#endif diff --git a/arm9/source/emucore/ECSKeyboard.cpp b/arm9/source/emucore/ECSKeyboard.cpp new file mode 100644 index 0000000..b9eafc7 --- /dev/null +++ b/arm9/source/emucore/ECSKeyboard.cpp @@ -0,0 +1,85 @@ + +#include "ECSKeyboard.h" + +const INT32 ECSKeyboard::sortedObjectIndices[NUM_ECS_OBJECTS] = { + 24, 29, 34, 28, 33, 27, 32, 26, 31, 25, 30, // ESCAPE, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 + 23, 22, 40, 21, 39, 20, 38, 19, 37, 18, // Q, W, E, R, T, Y, U, I, O, P + 46, 16, 45, 15, 44, 14, 43, 13, 42, 12, 36, // A, S, D, F, G, H, J, K, L, SEMICOLON, ENTER + 10, 4, 9, 3, 8, 2, 7, 1, 6, // Z, X, C, V, B, N, M, COMMA, PERIOD + 41, 47, 5, // CTRL, SHIFT, SPACE, + 17, 11, 0, 35, // UP, DOWN, LEFT, RIGHT +}; + +ECSKeyboard::ECSKeyboard(INT32 id) +: InputConsumer(id), + rowsToScan(0) +{ + memset(rowInputValues, 0, sizeof(rowInputValues)); +} + +ECSKeyboard::~ECSKeyboard() +{ +} + +INT32 ECSKeyboard::getInputConsumerObjectCount() +{ + return 0; +} + +InputConsumerObject* ECSKeyboard::getInputConsumerObject(INT32 i) +{ + return NULL; +} + +void ECSKeyboard::resetInputConsumer() +{ + rowsToScan = 0; + for (UINT16 i = 0; i < 8; i++) + rowInputValues[i] = 0; +} + +void ECSKeyboard::evaluateInputs() +{ + for (UINT16 row = 0; row < 6; row++) + { + rowInputValues[row] = 0; + if (inputConsumerObjects[row]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x01; + if (inputConsumerObjects[row+6]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x02; + if (inputConsumerObjects[row+12]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x04; + if (inputConsumerObjects[row+18]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x08; + if (inputConsumerObjects[row+24]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x10; + if (inputConsumerObjects[row+30]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x20; + if (inputConsumerObjects[row+36]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x40; + if (inputConsumerObjects[row+42]->getInputValue() == 1.0f) + rowInputValues[row] |= 0x80; + } + rowInputValues[6] = (inputConsumerObjects[47]->getInputValue() == 1.0f ? 0x80 : 0); +} + +UINT16 ECSKeyboard::getInputValue() +{ + UINT16 inputValue = 0; + UINT16 rowMask = 1; + for (UINT16 row = 0; row < 8; row++) { + if ((rowsToScan & rowMask) != 0) { + rowMask = (UINT16)(rowMask << 1); + continue; + } + inputValue |= rowInputValues[row]; + + rowMask = (UINT16)(rowMask << 1); + } + + return (UINT16)(0xFF ^ inputValue); +} + +void ECSKeyboard::setOutputValue(UINT16 value) { + this->rowsToScan = value; +} diff --git a/arm9/source/emucore/ECSKeyboard.h b/arm9/source/emucore/ECSKeyboard.h new file mode 100644 index 0000000..1a22e8d --- /dev/null +++ b/arm9/source/emucore/ECSKeyboard.h @@ -0,0 +1,49 @@ + +#ifndef ECSKEYBOARD_H +#define ECSKEYBOARD_H + +#define NUM_ECS_OBJECTS 48 + +#include "AY38914_InputOutput.h" +#include "InputConsumer.h" + +class EmulationDirector; + +class ECSKeyboard : public AY38914_InputOutput, public InputConsumer +{ + + friend class EmulationDirector; + + public: + ECSKeyboard(INT32 id); + virtual ~ECSKeyboard(); + + const CHAR* getName() { return "ECS Keyboard"; } + + void resetInputConsumer(); + + /** + * Poll the controller. This function is invoked by the + * InputConsumerBus just after the Emulator indicates it has entered + * vertical blank. + */ + void evaluateInputs(); + + //functions to get descriptive info about the input consumer + INT32 getInputConsumerObjectCount(); + InputConsumerObject* getInputConsumerObject(INT32 i); + + UINT16 getInputValue(); + void setOutputValue(UINT16 value); + + private: + InputConsumerObject* inputConsumerObjects[NUM_ECS_OBJECTS]; + UINT16 rowsToScan; + UINT16 rowInputValues[8]; + + static const INT32 sortedObjectIndices[NUM_ECS_OBJECTS]; + +}; + +#endif + diff --git a/arm9/source/emucore/Emulator.cpp b/arm9/source/emucore/Emulator.cpp new file mode 100644 index 0000000..97e87c9 --- /dev/null +++ b/arm9/source/emucore/Emulator.cpp @@ -0,0 +1,264 @@ + +#include "Emulator.h" +#include "Intellivision.h" + +extern UINT32 systemIDs[NUM_EMULATORS]; +extern Emulator* emus[NUM_EMULATORS]; + +UINT32 Emulator::GetEmulatorCount() +{ + return NUM_EMULATORS; +} + +Emulator* Emulator::GetEmulator(UINT32 i) +{ + return emus[i]; +} + +Emulator* Emulator::GetEmulatorByID(UINT32 targetSystemID) +{ + for (int i = 0; i < NUM_EMULATORS; i++) { + if (systemIDs[i] == targetSystemID) + return emus[i]; + } + + return NULL; +} + +Emulator::Emulator(const char* name) + : Peripheral(name, name), + currentRip(NULL), + peripheralCount(0) +{ + memset(peripherals, 0, sizeof(peripherals)); + memset(usePeripheralIndicators, FALSE, sizeof(usePeripheralIndicators)); + printf("Emulator [%s] Constructed\n", name); +} + +void Emulator::AddPeripheral(Peripheral* p) +{ + peripherals[peripheralCount] = p; + printf(" Peripheral [%d] Added\n", peripheralCount); + peripheralCount++; +} + +UINT32 Emulator::GetPeripheralCount() +{ + return peripheralCount; +} + +Peripheral* Emulator::GetPeripheral(UINT32 i) +{ + return peripherals[i]; +} + +void Emulator::UsePeripheral(UINT32 i, BOOL b) +{ + usePeripheralIndicators[i] = b; +} + +UINT32 Emulator::GetVideoWidth() +{ + return videoWidth; +} + +UINT32 Emulator::GetVideoHeight() +{ + return videoHeight; +} + +void Emulator::InitVideo(VideoBus* video, UINT32 width, UINT32 height) +{ + if ( video != NULL ) { + videoBus = video; + } + + videoBus->init(width, height); +} + +void Emulator::ReleaseVideo() +{ + if (videoBus) { + videoBus->release(); + videoBus = NULL; + } +} + +void Emulator::InitAudio(AudioMixer* audio, UINT32 sampleRate) +{ + if (audio != NULL) { + audioMixer = audio; + } + + // TODO: check for an existing audioMixer processor and release it + for (UINT16 i = 0; i < GetProcessorCount(); i++) { + Processor* p = GetProcessor(i); + if (p == audio) { + RemoveProcessor(audio); + } + } + + AddProcessor(audioMixer); + audioMixer->init(sampleRate); +} + +void Emulator::ReleaseAudio() +{ + if (audioMixer) { + audioMixer->release(); + RemoveProcessor(audioMixer); + audioMixer = NULL; + } +} + +void Emulator::Reset() +{ + processorBus.reset(); + memoryBus.reset(); +} + +void Emulator::SetRip(Rip* rip) +{ + if (this->currentRip != NULL) { + processorBus.removeAll(); + memoryBus.removeAll(); + videoBus->removeAll(); + audioMixer->removeAll(); + inputConsumerBus.removeAll(); + } + + currentRip = rip; + + //TODO: use the desired peripheral configuration specified by the rip + + if (this->currentRip != NULL) { + InsertPeripheral(this); + + //use the desired peripherals + for (INT32 i = 0; i < peripheralCount; i++) { + if (usePeripheralIndicators[i]) + InsertPeripheral(peripherals[i]); + } + + InsertPeripheral(currentRip); + } +} + +void Emulator::InsertPeripheral(Peripheral* p) +{ + UINT16 i; + + printf(" Peripheral Inserted: [%s]\n", p->GetName()); + + printf(" Processors [%d]\n", p->GetProcessorCount()); + //processors + UINT16 count = p->GetProcessorCount(); + for (i = 0; i < count; i++) + { + printf(" [%s]\n", p->GetProcessor(i)->getName()); + processorBus.addProcessor(p->GetProcessor(i)); + } + + printf(" RAM [%d]\n", p->GetRAMCount()); + //RAM + count = p->GetRAMCount(); + for (i = 0; i < count; i++) + { + printf(" @ [%04X] Size [%04X]\n", p->GetRAM(i)->getReadAddress(), p->GetRAM(i)->getReadSize()); + memoryBus.addMemory(p->GetRAM(i)); + } + + printf(" ROM [%d]\n", p->GetROMCount()); + //ROM + count = p->GetROMCount(); + for (i = 0; i < count; i++) + { + ROM* nextRom = p->GetROM(i); + printf(" [%s] @ [%04X] Size [%04X]\n", nextRom->getName(), nextRom->getReadAddress(), nextRom->getReadSize()); + if (!nextRom->isInternal()) + memoryBus.addMemory(nextRom); + } + + printf(" VIDEO PRODUCERS [%d]\n", p->GetVideoProducerCount()); + //video producers + count = p->GetVideoProducerCount(); + for (i = 0; i < count; i++) + videoBus->addVideoProducer(p->GetVideoProducer(i)); + + printf(" AUDIO PRODUCERS [%d]\n", p->GetAudioProducerCount()); + //audio producers + count = p->GetAudioProducerCount(); + for (i = 0; i < count; i++) + audioMixer->addAudioProducer(p->GetAudioProducer(i)); + + printf(" INPUT CONSUMERS [%d]\n", p->GetInputConsumerCount()); + //input consumers + count = p->GetInputConsumerCount(); + for (i = 0; i < count; i++) + { + inputConsumerBus.addInputConsumer(p->GetInputConsumer(i)); + printf(" [%s]\n", p->GetInputConsumer(i)->getName()); + } + printf(" Peripherals Done\n"); +} + +void Emulator::RemovePeripheral(Peripheral* p) +{ + UINT16 i; + + //processors + UINT16 count = p->GetProcessorCount(); + for (i = 0; i < count; i++) + processorBus.removeProcessor(p->GetProcessor(i)); + + //RAM + count = p->GetRAMCount(); + for (i = 0; i < count; i++) + memoryBus.removeMemory(p->GetRAM(i)); + + //ROM + count = p->GetROMCount(); + for (i = 0; i < count; i++) + memoryBus.removeMemory(p->GetROM(i)); + + //video producers + count = p->GetVideoProducerCount(); + for (i = 0; i < count; i++) + videoBus->removeVideoProducer(p->GetVideoProducer(i)); + + //audio producers + count = p->GetAudioProducerCount(); + for (i = 0; i < count; i++) + audioMixer->removeAudioProducer(p->GetAudioProducer(i)); + + //input consumers + count = p->GetInputConsumerCount(); + for (i = 0; i < count; i++) + inputConsumerBus.removeInputConsumer(p->GetInputConsumer(i)); +} + +void Emulator::Run() +{ + inputConsumerBus.evaluateInputs(); + processorBus.run(); +} + +void Emulator::Render() +{ + videoBus->render(); +} + +void Emulator::FlushAudio() +{ + audioMixer->flushAudio(); +} + +UINT32 Emulator::systemIDs[NUM_EMULATORS] = { + ID_SYSTEM_INTELLIVISION, + }; + +Intellivision Emulator::inty; + +Emulator* Emulator::emus[] = { + &inty, +}; diff --git a/arm9/source/emucore/Emulator.h b/arm9/source/emucore/Emulator.h new file mode 100644 index 0000000..81944fe --- /dev/null +++ b/arm9/source/emucore/Emulator.h @@ -0,0 +1,110 @@ + +#ifndef EMULATOR_H +#define EMULATOR_H + +#include "Peripheral.h" +#include "types.h" +#include "Rip.h" +#include "ProcessorBus.h" +#include "Processor.h" +#include "AudioMixer.h" +#include "AudioProducer.h" +#include "InputConsumerBus.h" +#include "InputConsumer.h" +#include "VideoBus.h" +#include "VideoProducer.h" +#include "MemoryBus.h" +#include "Memory.h" + +typedef struct _StateHeader +{ + UINT32 emu; + UINT32 state; + UINT32 emuID; + UINT32 version; + UINT32 sys; + UINT32 sysID; + UINT32 cart; + UINT32 cartID; +} StateHeader; + +typedef struct _StateChunk +{ + UINT32 id; + UINT32 size; +} StateChunk; + +#if defined(DEBUG) +#define EMU_STATE_VERSION ('dev\0') +#else +#define EMU_STATE_VERSION (0x02010000) +#endif + +class Intellivision; + +#define MAX_PERIPHERALS 16 +#define NUM_EMULATORS 1 + +/** + * + */ +class Emulator : public Peripheral +{ + public: + void AddPeripheral(Peripheral* p); + UINT32 GetPeripheralCount(); + Peripheral* GetPeripheral(UINT32); + + UINT32 GetVideoWidth(); + UINT32 GetVideoHeight(); + + void UsePeripheral(UINT32, BOOL); + + void SetRip(Rip* rip); + + void InitVideo(VideoBus* video, UINT32 width, UINT32 height); + void ReleaseVideo(); + void InitAudio(AudioMixer* audio, UINT32 sampleRate); + void ReleaseAudio(); + + void Reset(); + void Run(); + void FlushAudio(); + void Render(); + + virtual BOOL SaveState(const CHAR* filename) = 0; + virtual BOOL LoadState(const CHAR* filename) = 0; + + static UINT32 GetEmulatorCount(); + static Emulator* GetEmulator(UINT32 i); + static Emulator* GetEmulatorByID(UINT32 targetSystemID); + + protected: + Emulator(const char* name); + + MemoryBus memoryBus; + + Rip* currentRip; + + UINT32 videoWidth; + UINT32 videoHeight; + + private: + ProcessorBus processorBus; + AudioMixer *audioMixer; + VideoBus *videoBus; + InputConsumerBus inputConsumerBus; + + void InsertPeripheral(Peripheral* p); + void RemovePeripheral(Peripheral* p); + + Peripheral* peripherals[MAX_PERIPHERALS]; + BOOL usePeripheralIndicators[MAX_PERIPHERALS]; + INT32 peripheralCount; + + static UINT32 systemIDs[NUM_EMULATORS]; + static Emulator* emus[NUM_EMULATORS]; + static Intellivision inty; +}; + +#endif diff --git a/arm9/source/emucore/GRAM.cpp b/arm9/source/emucore/GRAM.cpp new file mode 100644 index 0000000..728ad80 --- /dev/null +++ b/arm9/source/emucore/GRAM.cpp @@ -0,0 +1,79 @@ +#include +#include "GRAM.h" + +#define GRAM_ADDRESS 0x3800 +#define GRAM_READ_MASK 0xF9FF +#define GRAM_WRITE_MASK 0x39FF + +UINT8 gram_image[GRAM_SIZE] __attribute__((section(".dtcm"))); +UINT8 dirtyCards[GRAM_SIZE>>3] __attribute__((section(".dtcm"))); +UINT8 dirtyRAM __attribute__((section(".dtcm"))); + +GRAM::GRAM() +: RAM(GRAM_SIZE, GRAM_ADDRESS, GRAM_READ_MASK, GRAM_WRITE_MASK) +{} + +void GRAM::reset() +{ + UINT16 i; + dirtyRAM = TRUE; + for (i = 0; i < GRAM_SIZE; i++) + gram_image[i] = 0; + + for (i = 0; i < 0x40; i++) + dirtyCards[i] = TRUE; +} + +void GRAM::poke(UINT16 location, UINT16 value) +{ + location &= 0x01FF; + //value &= 0xFF; + + gram_image[location] = (UINT8)value; + dirtyCards[location>>3] = TRUE; + dirtyRAM = TRUE; +} + +void GRAM::markClean() { + if (!dirtyRAM) + return; + + for (UINT16 i = 0; i < 0x40; i++) + dirtyCards[i] = FALSE; + dirtyRAM = FALSE; +} + +BOOL GRAM::isDirty() { + return dirtyRAM; +} + +BOOL GRAM::isCardDirty(UINT16 cardLocation) { + return dirtyCards[cardLocation>>3]; +} + +RAMState GRAM::getState(UINT16* image) +{ + RAMState state = {0}; +#if 0 + state = RAM::getState(NULL); + + if (image != NULL) { + this->getImage(image, 0, this->getImageByteSize()); + } +#endif + return state; +} + +void GRAM::setState(RAMState state, UINT16* image) +{ +#if 0 + RAM::setState(state, NULL); + + if (image != NULL) { + this->setImage(image, 0, this->getImageByteSize()); + } + + memset(this->dirtyCards, TRUE, sizeof(this->dirtyCards)); + this->dirtyRAM = TRUE; +#endif +} diff --git a/arm9/source/emucore/GRAM.h b/arm9/source/emucore/GRAM.h new file mode 100644 index 0000000..45c36f7 --- /dev/null +++ b/arm9/source/emucore/GRAM.h @@ -0,0 +1,44 @@ + +#ifndef GRAM_H +#define GRAM_H + +#include "RAM.h" + +#define GRAM_SIZE 0x0200 + +extern UINT8 gram_image[GRAM_SIZE]; +extern UINT8 dirtyCards[GRAM_SIZE>>3]; +extern UINT8 dirtyRAM; + +class GRAM : public RAM +{ + friend class AY38900; + + public: + GRAM(); + + void reset(); + inline UINT16 peek(UINT16 location) {return gram_image[location & 0x01FF];} + void poke(UINT16 location, UINT16 value); + + void markClean(); + BOOL isDirty(); + BOOL isCardDirty(UINT16 cardLocation); + + inline size_t getImageByteSize() { + return size * sizeof(UINT16); + } + void getImage(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, gram_image + offset, size); + } + void setImage(void* src, UINT16 offset, UINT16 size) { + memcpy(gram_image + offset, src, size); + } + + RAMState getState(UINT16* image); + void setState(RAMState state, UINT16* image); + + private: +}; + +#endif diff --git a/arm9/source/emucore/GROM.cpp b/arm9/source/emucore/GROM.cpp new file mode 100644 index 0000000..82d451f --- /dev/null +++ b/arm9/source/emucore/GROM.cpp @@ -0,0 +1,15 @@ + +#include "GROM.h" + +GROM::GROM() +: ROM("GROM", "grom.bin", 0, 1, GROM_SIZE, GROM_ADDRESS) +{} + +void GROM::reset() +{ + visible = TRUE; +} + + + + diff --git a/arm9/source/emucore/GROM.h b/arm9/source/emucore/GROM.h new file mode 100644 index 0000000..0985252 --- /dev/null +++ b/arm9/source/emucore/GROM.h @@ -0,0 +1,27 @@ + +#ifndef GROM_H +#define GROM_H + +#include "Memory.h" +#include "ROM.h" + +#define GROM_SIZE 0x0800 +#define GROM_ADDRESS 0x3000 + +class GROM : public ROM +{ + + friend class AY38900; + + public: + GROM(); + + void reset(); + inline UINT16 peek(UINT16 location) {return ROM::peek(location);} + + private: + BOOL visible; + +}; + +#endif diff --git a/arm9/source/emucore/HandController.cpp b/arm9/source/emucore/HandController.cpp new file mode 100644 index 0000000..ed68671 --- /dev/null +++ b/arm9/source/emucore/HandController.cpp @@ -0,0 +1,107 @@ + +#include "HandController.h" +#include "Intellivision.h" + +const UINT16 HandController::BUTTON_OUTPUT_VALUES[15] = { + 0x81, //OUTPUT_KEYPAD_ONE + 0x41, //OUTPUT_KEYPAD_TWO + 0x21, //OUTPUT_KEYPAD_THREE + 0x82, //OUTPUT_KEYPAD_FOUR + 0x42, //OUTPUT_KEYPAD_FIVE + 0x22, //OUTPUT_KEYPAD_SIX + 0x84, //OUTPUT_KEYPAD_SEVEN + 0x44, //OUTPUT_KEYPAD_EIGHT + 0x24, //OUTPUT_KEYPAD_NINE + 0x88, //OUTPUT_KEYPAD_CLEAR + 0x48, //OUTPUT_KEYPAD_ZERO + 0x28, //OUTPUT_KEYPAD_ENTER + 0xA0, //OUTPUT_ACTION_BUTTON_TOP + 0x60, //OUTPUT_ACTION_BUTTON_BOTTOM_LEFT + 0xC0 //OUTPUT_ACTION_BUTTON_BOTTOM_RIGHT +}; + +const UINT16 HandController::DIRECTION_OUTPUT_VALUES[16] = { + 0x04, //OUTPUT_DISC_NORTH + 0x14, //OUTPUT_DISC_NORTH_NORTH_EAST + 0x16, //OUTPUT_DISC_NORTH_EAST + 0x06, //OUTPUT_DISC_EAST_NORTH_EAST + 0x02, //OUTPUT_DISC_EAST + 0x12, //OUTPUT_DISC_EAST_SOUTH_EAST + 0x13, //OUTPUT_DISC_SOUTH_EAST + 0x03, //OUTPUT_DISC_SOUTH_SOUTH_EAST + 0x01, //OUTPUT_DISC_SOUTH + 0x11, //OUTPUT_DISC_SOUTH_SOUTH_WEST + 0x19, //OUTPUT_DISC_SOUTH_WEST + 0x09, //OUTPUT_DISC_WEST_SOUTH_WEST + 0x08, //OUTPUT_DISC_WEST + 0x18, //OUTPUT_DISC_WEST_NORTH_WEST + 0x1C, //OUTPUT_DISC_NORTH_WEST + 0x0C //OUTPUT_DISC_NORTH_NORTH_WEST +}; + +HandController::HandController(INT32 id, const CHAR* n) +: InputConsumer(id), + name(n) +{ +} + +HandController::~HandController() +{ +} + +INT32 HandController::getInputConsumerObjectCount() +{ + return 0; +} + +InputConsumerObject* HandController::getInputConsumerObject(INT32 i) +{ + return NULL; +} + +int ds_key_input[16] = {0}; // Set to '1' if pressed... 0 if released +int ds_disc_input[16] = {0}; // Set to '1' if pressed... 0 if released. + +void HandController::evaluateInputs() +{ + inputValue = 0; + bool keypad_active = false; + + // TODO: this is a bit of a hack... setup 2 sets of inputs or don't instantiate the 2nd controller... + if (strcmp(name, "Hand Controller #1") == 0) + { + for (UINT16 i = 0; i < 15; i++) + { + if (ds_key_input[i]) + { + if (i <= 11) keypad_active=true; + inputValue |= BUTTON_OUTPUT_VALUES[i]; + } + } + + // Keypad has priority over disc... + if (!keypad_active) + { + for (UINT16 i = 0; i < 16; i++) + { + if (ds_disc_input[i]) inputValue |= DIRECTION_OUTPUT_VALUES[i]; + } + } + } + + inputValue = (UINT16)(0xFF ^ inputValue); +} + +void HandController::resetInputConsumer() +{ + inputValue = 0xFF; +} + +void HandController::setOutputValue(UINT16) +{} + +UINT16 HandController::getInputValue() +{ + return inputValue; +} + diff --git a/arm9/source/emucore/HandController.h b/arm9/source/emucore/HandController.h new file mode 100644 index 0000000..de36b9c --- /dev/null +++ b/arm9/source/emucore/HandController.h @@ -0,0 +1,77 @@ + +#ifndef HANDCONTROLLER_H +#define HANDCONTROLLER_H + +#define NUM_HAND_CONTROLLER_OBJECTS 23 + +#include "AY38914_InputOutput.h" +#include "InputConsumer.h" + +class HandController : public AY38914_InputOutput, public InputConsumer +{ + + public: + HandController(INT32 id, const CHAR* n); + virtual ~HandController(); + + const CHAR* getName() { return name; } + + void resetInputConsumer(); + + /** + * Poll the controller. This function is invoked by the + * InputConsumerBus just after the Emulator indicates it has entered + * vertical blank. + */ + void evaluateInputs(); + + //functions to get descriptive info about the input consumer + INT32 getInputConsumerObjectCount(); + InputConsumerObject* getInputConsumerObject(INT32 i); + + void setOutputValue(UINT16 value); + UINT16 getInputValue(); + + private: + const CHAR* name; + + InputConsumerObject* inputConsumerObjects[NUM_HAND_CONTROLLER_OBJECTS]; + UINT16 inputValue; + + static const UINT16 BUTTON_OUTPUT_VALUES[15]; + static const UINT16 DIRECTION_OUTPUT_VALUES[16]; + +}; + +// jeremiah sypult +enum +{ + CONTROLLER_DISC_DOWN = 0x01, + CONTROLLER_DISC_RIGHT = 0x02, + CONTROLLER_DISC_UP = 0x04, + CONTROLLER_DISC_LEFT = 0x08, + CONTROLLER_DISC_WIDE = 0x10, + CONTROLLER_DISC_UP_LEFT = 0x1C, + CONTROLLER_DISC_UP_RIGHT = 0x16, + CONTROLLER_DISC_DOWN_LEFT = 0x19, + CONTROLLER_DISC_DOWN_RIGHT = 0x13, + + CONTROLLER_KEYPAD_ONE = 0x81, + CONTROLLER_KEYPAD_TWO = 0x41, + CONTROLLER_KEYPAD_THREE = 0x21, + CONTROLLER_KEYPAD_FOUR = 0x82, + CONTROLLER_KEYPAD_FIVE = 0x42, + CONTROLLER_KEYPAD_SIX = 0x22, + CONTROLLER_KEYPAD_SEVEN = 0x84, + CONTROLLER_KEYPAD_EIGHT = 0x44, + CONTROLLER_KEYPAD_NINE = 0x24, + CONTROLLER_KEYPAD_CLEAR = 0x88, + CONTROLLER_KEYPAD_ZERO = 0x48, + CONTROLLER_KEYPAD_ENTER = 0x28, + + CONTROLLER_ACTION_TOP = 0xA0, + CONTROLLER_ACTION_BOTTOM_LEFT = 0x60, + CONTROLLER_ACTION_BOTTOM_RIGHT = 0xC0 +}; + +#endif diff --git a/arm9/source/emucore/InputConsumer.h b/arm9/source/emucore/InputConsumer.h new file mode 100644 index 0000000..4ffd08a --- /dev/null +++ b/arm9/source/emucore/InputConsumer.h @@ -0,0 +1,42 @@ + +#ifndef INPUTCONSUMER_H +#define INPUTCONSUMER_H + +#include "InputConsumerObject.h" +#include "types.h" + +/** + * A InputConsumer is a gaming input device, such as the hand controllers for + * the Intellivision and Atari 5200 or the joysticks and paddles for the + * Atari2600. + */ +class InputConsumer +{ + +friend class InputConsumerBus; + +public: + InputConsumer(INT32 i) : id(i) {} + + INT32 getId() { return id; } + + virtual const CHAR* getName() = 0; + + virtual void resetInputConsumer() = 0; + + /** + * Poll the controller. This function is invoked by the + * InputConsumerBus just after the Emulator indicates it has entered + * vertical blank. + */ + virtual void evaluateInputs() = 0; + + //functions to get descriptive info about the input consumer + virtual INT32 getInputConsumerObjectCount() = 0; + virtual InputConsumerObject* getInputConsumerObject(INT32 i) = 0; + +private: + INT32 id; +}; + +#endif diff --git a/arm9/source/emucore/InputConsumerBus.cpp b/arm9/source/emucore/InputConsumerBus.cpp new file mode 100644 index 0000000..ff0f7ac --- /dev/null +++ b/arm9/source/emucore/InputConsumerBus.cpp @@ -0,0 +1,46 @@ + +#include "stdio.h" +#include "InputConsumerBus.h" + +InputConsumerBus::InputConsumerBus() +{ + inputConsumerCount = 0; +} + +void InputConsumerBus::addInputConsumer(InputConsumer* p) +{ + inputConsumers[inputConsumerCount] = p; + inputConsumerCount++; +} + +void InputConsumerBus::removeInputConsumer(InputConsumer* p) +{ + for (UINT32 i = 0; i < inputConsumerCount; i++) { + if (inputConsumers[i] == p) { + for (UINT32 j = i; j < (inputConsumerCount-1); j++) + inputConsumers[j] = inputConsumers[j+1]; + inputConsumerCount--; + return; + } + } +} + +void InputConsumerBus::removeAll() +{ + while (inputConsumerCount) + removeInputConsumer(inputConsumers[0]); +} + +void InputConsumerBus::reset() +{ + for (UINT32 i = 0; i < inputConsumerCount; i++) + inputConsumers[i]->resetInputConsumer(); +} + +void InputConsumerBus::evaluateInputs() +{ + //tell each of the input consumers that they may now pull their data from + //the input device + for (UINT32 i = 0; i < inputConsumerCount; i++) + inputConsumers[i]->evaluateInputs(); +} diff --git a/arm9/source/emucore/InputConsumerBus.h b/arm9/source/emucore/InputConsumerBus.h new file mode 100644 index 0000000..712caf6 --- /dev/null +++ b/arm9/source/emucore/InputConsumerBus.h @@ -0,0 +1,27 @@ + +#ifndef INPUTCONSUMERBUS_H +#define INPUTCONSUMERBUS_H + +#include "InputConsumer.h" + +const INT32 MAX_INPUT_CONSUMERS = 10; + +class InputConsumerBus +{ + + public: + InputConsumerBus(); + void reset(); + void evaluateInputs(); + + void addInputConsumer(InputConsumer* ic); + void removeInputConsumer(InputConsumer* ic); + void removeAll(); + + private: + InputConsumer* inputConsumers[MAX_INPUT_CONSUMERS]; + UINT32 inputConsumerCount; + +}; + +#endif diff --git a/arm9/source/emucore/InputConsumerObject.cpp b/arm9/source/emucore/InputConsumerObject.cpp new file mode 100644 index 0000000..329fb08 --- /dev/null +++ b/arm9/source/emucore/InputConsumerObject.cpp @@ -0,0 +1,65 @@ +#include +#include + +// TODO: jeremiah sypult cross-platform +#if defined( _WIN32 ) +#include +#endif + +#include "InputConsumerObject.h" + +InputConsumerObject::InputConsumerObject(INT32 i, const CHAR* n, GUID ddg, INT32 doid) +: id(i), + name(n), + defaultDeviceGuid(ddg), + defaultObjectID(doid), + bindingCount(0) +{ + memset(producerBindings, 0, sizeof(producerBindings)); + memset(objectIDBindings, 0, sizeof(objectIDBindings)); + memset(subBindingCounts, 0, sizeof(subBindingCounts)); +} + +InputConsumerObject::~InputConsumerObject() +{ + clearBindings(); +} + +void InputConsumerObject::addBinding(InputProducer** producers, INT32* objectids, INT32 count) +{ + if (bindingCount >= MAX_BINDINGS) + return; + + producerBindings[bindingCount] = new InputProducer*[count]; + memcpy(producerBindings[bindingCount], producers, sizeof(InputProducer*)*count); + objectIDBindings[bindingCount] = new INT32[count]; + memcpy(objectIDBindings[bindingCount], objectids, sizeof(INT32)*count); + subBindingCounts[bindingCount] = count; + bindingCount++; +} + +void InputConsumerObject::clearBindings() +{ + for (INT32 i = 0; i < bindingCount; i++) { + delete[] producerBindings[i]; + delete[] objectIDBindings[i]; + } + memset(producerBindings, 0, sizeof(producerBindings)); + memset(objectIDBindings, 0, sizeof(objectIDBindings)); + memset(subBindingCounts, 0, sizeof(subBindingCounts)); + bindingCount = 0; +} + +float InputConsumerObject::getInputValue() +{ + float value = 0.0f; + for (INT32 i = 0; i < bindingCount; i++) { + float nextValue = 1.0f; + for (INT32 j = 0; j < subBindingCounts[i]; j++) { + float v = producerBindings[i][j]->getValue(objectIDBindings[i][j]); + nextValue = (nextValue < v ? nextValue : v); + } + value = (value != 0 && value > nextValue ? value : nextValue); + } + return value; +} diff --git a/arm9/source/emucore/InputConsumerObject.h b/arm9/source/emucore/InputConsumerObject.h new file mode 100644 index 0000000..77e8fb6 --- /dev/null +++ b/arm9/source/emucore/InputConsumerObject.h @@ -0,0 +1,53 @@ + +#ifndef INPUTCONSUMEROBJECT_H +#define INPUTCONSUMEROBJECT_H + +#include "InputProducer.h" +#include "types.h" + +#define MAX_BINDINGS 10 + +class InputConsumerObject +{ + friend class InputConsumer; + +public: + InputConsumerObject(INT32 id, const CHAR* name, GUID defaultDeviceGuid, INT32 defaultObjectID); + virtual ~InputConsumerObject(); + + INT32 getId() { return id; } + + const CHAR* getName() { return name; } + + GUID getDefaultDeviceGuid() { return defaultDeviceGuid; } + + INT32 getDefaultEnum() { return defaultObjectID; } + + INT32 getBindingCount() { return bindingCount; } + + INT32 getSubBindingCount(INT32 i) { return subBindingCounts[i]; } + + InputProducer* getSubBindingProducer(INT32 i, INT32 j) { return producerBindings[i][j]; } + + INT32 getSubBindingEnum(INT32 i, INT32 j) { return objectIDBindings[i][j]; } + + void addBinding(InputProducer** producer, INT32* objectid, INT32 count); + + void clearBindings(); + + float getInputValue(); + +private: + INT32 id; + const CHAR* name; + GUID defaultDeviceGuid; + INT32 defaultObjectID; + + InputProducer** producerBindings[MAX_BINDINGS]; + INT32* objectIDBindings[MAX_BINDINGS]; + INT32 subBindingCounts[MAX_BINDINGS]; + INT32 bindingCount; + +}; + +#endif \ No newline at end of file diff --git a/arm9/source/emucore/InputProducer.h b/arm9/source/emucore/InputProducer.h new file mode 100644 index 0000000..baf7f1f --- /dev/null +++ b/arm9/source/emucore/InputProducer.h @@ -0,0 +1,38 @@ + +#ifndef INPUTPRODUCER_H +#define INPUTPRODUCER_H + +// TODO: jeremiah sypult cross-platform +#if defined( _WIN32 ) +#include +#endif + +#include "types.h" + +class InputProducer +{ +public: + InputProducer(GUID g) : guid(g) {} + + GUID getGuid() { return guid; } + + virtual const CHAR* getName() = 0; +// TODO: jeremiah sypult cross-platform +#if defined( _WIN32 ) + virtual IDirectInputDevice8* getDevice() = 0; +#endif + virtual void poll() = 0; + + virtual INT32 getInputCount() = 0; + + virtual const CHAR* getInputName(INT32) = 0; + + virtual float getValue(INT32 enumeration) = 0; + + virtual BOOL isKeyboardDevice() = 0; + +private: + GUID guid; +}; + +#endif \ No newline at end of file diff --git a/arm9/source/emucore/InputProducerManager.cpp b/arm9/source/emucore/InputProducerManager.cpp new file mode 100644 index 0000000..5e10a57 --- /dev/null +++ b/arm9/source/emucore/InputProducerManager.cpp @@ -0,0 +1,60 @@ +#include +#include +#include "InputProducerManager.h" + +#if defined( _WIN32 ) +InputProducerManager::InputProducerManager(HWND w) +: wnd(w) +{ + DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&directInput, NULL); +} +#endif + +InputProducerManager::~InputProducerManager() +{ +#if defined( _WIN32 ) + directInput->Release(); +#endif +} + +#if defined( _WIN32 ) +IDirectInput8* InputProducerManager::getDevice() +{ + return directInput; +} +#endif + +InputProducer* InputProducerManager::acquireInputProducer(GUID deviceGuid) +{ + return NULL; +} + +INT32 InputProducerManager::getAcquiredInputProducerCount() +{ + return (INT32)devices.size(); +} + +InputProducer* InputProducerManager::getAcquiredInputProducer(INT32 i) +{ + return devices[i]; +} + +void InputProducerManager::pollInputProducers() +{ + for (vector::iterator it = devices.begin(); it < devices.end(); it++) + (*it)->poll(); +} + +void InputProducerManager::releaseInputProducers() +{ + for (vector::iterator it = devices.begin(); it < devices.end(); it++) { +#if defined( _WIN32 ) + IDirectInputDevice8* nextDevice = (*it)->getDevice(); + nextDevice->Unacquire(); + nextDevice->Release(); + delete (*it); +#endif + } + devices.clear(); +} + diff --git a/arm9/source/emucore/InputProducerManager.h b/arm9/source/emucore/InputProducerManager.h new file mode 100644 index 0000000..ef7b843 --- /dev/null +++ b/arm9/source/emucore/InputProducerManager.h @@ -0,0 +1,43 @@ + +#ifndef INPUTPRODUCERMANAGER_H +#define INPUTPRODUCERMANAGER_H + +// TODO: jeremiah sypult cross-platform +#if defined( _WIN32 ) +#include +#include +#endif + +#include "InputProducer.h" +#include +using namespace std; + +class InputProducerManager +{ + +public: +#if defined( _WIN32 ) + InputProducerManager(HWND wnd); +#endif + virtual ~InputProducerManager(); +#if defined( _WIN32 ) + IDirectInput8* getDevice(); +#endif + InputProducer* acquireInputProducer(GUID deviceGuid); + INT32 getAcquiredInputProducerCount(); + InputProducer* getAcquiredInputProducer(INT32 i); + + void pollInputProducers(); + + void releaseInputProducers(); + +private: +#if defined( _WIN32 ) + HWND wnd; + IDirectInput8* directInput; +#endif + vector devices; + +}; + +#endif \ No newline at end of file diff --git a/arm9/source/emucore/Intellivision.cpp b/arm9/source/emucore/Intellivision.cpp new file mode 100644 index 0000000..704c1b2 --- /dev/null +++ b/arm9/source/emucore/Intellivision.cpp @@ -0,0 +1,336 @@ + +#include "Intellivision.h" + +TYPEDEF_STRUCT_PACK( _IntellivisionState +{ + StateHeader header; + StateChunk cpu; + CP1610State cpuState; + StateChunk stic; + AY38900State sticState; + StateChunk psg; + AY38914State psgState; + StateChunk RAM8bit; + RAMState RAM8bitState; + UINT16 RAM8bitImage[RAM8BIT_SIZE]; + StateChunk RAM16bit; + RAMState RAM16bitState; + UINT16 RAM16bitImage[RAM16BIT_SIZE]; + StateChunk GRAM; + RAMState GRAMState; + UINT16 GRAMImage[GRAM_SIZE]; + StateChunk ivoice; + IntellivoiceState ivoiceState; + StateChunk ecs; + ECSState ecsState; + StateChunk eof; +} IntellivisionState; ) + +/** + * Initializes all of the basic hardware included in the Intellivision + * Master Component as well as the ECS and Intellivoice peripherals. + * This method is called only once when the Intellivision is constructed. + */ +Intellivision::Intellivision() + : Emulator("Intellivision"), + player1Controller(0, "Hand Controller #1"), + player2Controller(1, "Hand Controller #2"), + psg(0x01F0, &player1Controller, &player2Controller), + RAM8bit(RAM8BIT_SIZE, 0x0100, 8), + RAM16bit(RAM16BIT_SIZE, 0x0200, 16), + execROM("Executive ROM", "exec.bin", 0, 2, 0x1000, 0x1000), + grom("GROM", "grom.bin", 0, 1, 0x0800, 0x3000), + gram(), + cpu(&memoryBus, 0x1000, 0x1004), + stic(&memoryBus, &grom, &gram) +{ + // define the video pixel dimensions + videoWidth = 160; + videoHeight = 192; + + //make the pin connections from the CPU to the STIC + stic.connectPinOut(AY38900_PIN_OUT_SR1, &cpu, CP1610_PIN_IN_INTRM); + stic.connectPinOut(AY38900_PIN_OUT_SR2, &cpu, CP1610_PIN_IN_BUSRQ); + cpu.connectPinOut(CP1610_PIN_OUT_BUSAK, &stic, AY38900_PIN_IN_SST); + + //add the player one hand controller + AddInputConsumer(&player1Controller); + + //add the player two hand controller + AddInputConsumer(&player2Controller); + + //add the 8-bit RAM + AddRAM(&RAM8bit); + + //add the 16-bit RAM + AddRAM(&RAM16bit); + + //add the executive ROM + AddROM(&execROM); + + //add the GROM + AddROM(&grom); + + //add the GRAM + AddRAM(&gram); + + //add the backtab ram + AddRAM(&stic.backtab); + + //add the CPU + AddProcessor(&cpu); + + //add the STIC + AddProcessor(&stic); + AddVideoProducer(&stic); + + //add the STIC registers + AddRAM(&stic.registers); + + //add the PSG + AddProcessor(&psg); + AddAudioProducer(&psg); + + //add the PSG registers + AddRAM(&psg.registers); + + AddPeripheral(&ecs); + AddPeripheral(&intellivoice); +} + +BOOL Intellivision::SaveState(const CHAR* filename) +{ + BOOL didSave = FALSE; + IntellivisionState state = {9}; + size_t totalStateSize = sizeof(IntellivisionState); + + size_t hsize = sizeof(StateHeader); + size_t csize = sizeof(StateChunk); + size_t cpusize = sizeof(CP1610State); + size_t sticsize = sizeof(AY38900State); + size_t psgsize = sizeof(AY38914State); + size_t ivoicesize = sizeof(IntellivoiceState); + size_t ecssize = sizeof(ECSState); + size_t ramsize = sizeof(RAMState); + size_t ram8imgsize = sizeof(state.RAM8bitImage); + size_t ram16imgsize = sizeof(state.RAM16bitImage); + size_t gramimgsize = sizeof(state.GRAMImage); + + state.header.emu = FOURCHAR('EMUS'); + state.header.state = FOURCHAR('TATE'); + state.header.emuID = ID_EMULATOR_BLISS; + state.header.version = FOURCHAR(EMU_STATE_VERSION); + state.header.sys = FOURCHAR('SYS\0'); + state.header.sysID = ID_SYSTEM_INTELLIVISION; + state.header.cart = FOURCHAR('CART'); + state.header.cartID = currentRip->GetCRC(); + + state.cpu.id = FOURCHAR('CPU\0'); + state.cpu.size = sizeof(CP1610State); + state.cpuState = cpu.getState(); + + state.stic.id = FOURCHAR('STIC'); + state.stic.size = sizeof(AY38900State); + state.sticState = stic.getState(); + + state.psg.id = FOURCHAR('PSG\0'); + state.psg.size = sizeof(AY38914State); + state.psgState = psg.getState(); + + state.RAM8bit.id = FOURCHAR('RAM0'); + state.RAM8bit.size = sizeof(RAMState) + sizeof(state.RAM8bitImage); + state.RAM8bitState = RAM8bit.getState(state.RAM8bitImage); + + state.RAM16bit.id = FOURCHAR('RAM1'); + state.RAM16bit.size = sizeof(RAMState) + sizeof(state.RAM16bitImage); + state.RAM16bitState = RAM16bit.getState(state.RAM16bitImage); + + state.GRAM.id = FOURCHAR('GRAM'); + state.GRAM.size = sizeof(RAMState) + sizeof(state.GRAMImage); + state.GRAMState = gram.getState(state.GRAMImage); + + // TODO: only if ivoice is used for this cart? + state.ivoice.id = FOURCHAR('VOIC'); + state.ivoice.size = sizeof(IntellivoiceState); + state.ivoiceState = intellivoice.getState(); + + // TODO: only if ecs is used for this cart? + state.ecs.id = FOURCHAR('ECS\0'); + state.ecs.size = sizeof(ECSState); + state.ecsState = ecs.getState(); + + state.eof.id = FOURCHAR('EOF\0'); + state.eof.size = sizeof(IntellivisionState); + + FILE* file = fopen(filename, "wb"); + + if (file == NULL) { + printf("Error: Unable to create file %s\n", filename); + didSave = FALSE; + } + + if (file != NULL && totalStateSize == fwrite(&state, 1, totalStateSize, file)) { + didSave = TRUE; + } else { + printf("Error: could not write %zu bytes to file %s\n", totalStateSize, filename); + didSave = FALSE; + } + + if (file) { + fclose(file); + file = NULL; + } + + return didSave; +} + +BOOL Intellivision::LoadState(const CHAR* filename) +{ + BOOL didLoadState = FALSE; + IntellivisionState state = {9}; + size_t totalStateSize = sizeof(IntellivisionState); + + FILE* file = fopen(filename, "rb"); + + if (file == NULL) { + printf("Error: Unable to open file %s\n", filename); + return FALSE; + } +#if 0 + // read in the whole file + if (totalStateSize != fread(&state, 1, totalStateSize, file)) { + printf("Error: could not read state (%zu bytes) from file %s\n", totalStateSize, filename); + goto close; + } +#else + BOOL isParsing = FALSE; + StateChunk chunk = {0}; + + // read in the header + if (sizeof(StateHeader) != fread(&state, 1, sizeof(StateHeader), file)) { + printf("Error: could not read state header (%zu bytes) from file %s\n", totalStateSize, filename); + goto close; + } + + // validate file header + if (state.header.emu != FOURCHAR('EMUS') || state.header.state != FOURCHAR('TATE')) { + printf("Error: invalid header in file %s\n", filename); + goto close; + } + + if (state.header.emuID != ID_EMULATOR_BLISS) { + printf("Error: invalid emulator ID %x in file %s\n", state.header.emuID, filename); + goto close; + } + + if (FOURCHAR(EMU_STATE_VERSION) != FOURCHAR('dev\0') && state.header.version != FOURCHAR('dev\0') && state.header.version != FOURCHAR(EMU_STATE_VERSION)) { + printf("Error: invalid emulator version 0x%08x (expected 0x%08x) in file %s\n", state.header.version, EMU_STATE_VERSION, filename); + goto close; + } + + if (state.header.sys != FOURCHAR('SYS\0')) { + printf("Error: expected 'SYS ' chunk in file %s\n", filename); + goto close; + } + + if (state.header.sysID != ID_SYSTEM_INTELLIVISION) { + printf("Error: invalid system ID %x in file %s\n", state.header.sysID, filename); + goto close; + } + + if (state.header.cart != FOURCHAR('CART')) { + printf("Error: expected 'CART' chunk in file %s\n", filename); + goto close; + } + + if (state.header.cartID != 0x00000000 && state.header.cartID != currentRip->GetCRC()) { + printf("Error: cartridge mismatch in file %s\n", filename); + goto close; + } + + isParsing = TRUE; + while (isParsing) { + size_t fpos = ftell(file); + if (sizeof(StateChunk) != fread(&chunk, 1, sizeof(StateChunk), file)) { + isParsing = FALSE; + break; + } + + switch (chunk.id) { + default: + fpos = ftell(file); + break; + case FOURCHAR('CPU\0'): + if (chunk.size == sizeof(state.cpuState)) { + state.cpu = chunk; + fread(&state.cpuState, 1, state.cpu.size, file); + } + break; + case FOURCHAR('STIC'): + if (chunk.size == sizeof(state.sticState)) { + state.stic = chunk; + fread(&state.sticState, 1, state.stic.size, file); + } + break; + case FOURCHAR('PSG\0'): + if (chunk.size == sizeof(state.psgState)) { + state.psg = chunk; + fread(&state.psgState, 1, state.psg.size, file); + } + break; + case FOURCHAR('RAM0'): + if (chunk.size == sizeof(state.RAM8bitState) + sizeof(state.RAM8bitImage)) { + state.RAM8bit = chunk; + fread(&state.RAM8bitState, 1, state.RAM8bit.size, file); + } + break; + case FOURCHAR('RAM1'): + if (chunk.size == sizeof(state.RAM16bitState) + sizeof(state.RAM16bitImage)) { + state.RAM16bit = chunk; + fread(&state.RAM16bitState, 1, state.RAM16bit.size, file); + } + break; + case FOURCHAR('GRAM'): + if (chunk.size == sizeof(state.GRAMState) + sizeof(state.GRAMImage)) { + state.GRAM = chunk; + fread(&state.GRAMState, 1, state.GRAM.size, file); + } + break; + case FOURCHAR('VOIC'): + // TODO: only if ivoice/ecs is used for this cart? + if (chunk.size == sizeof(state.ivoiceState)) { + state.ivoice = chunk; + fread(&state.ivoiceState, 1, state.ivoice.size, file); + } + break; + case FOURCHAR('ECS\0'): + // TODO: only if ivoice/ecs is used for this cart? + if (chunk.size == sizeof(state.ecsState)) { + state.ecs = chunk; + fread(&state.ecsState, 1, state.ecs.size, file); + } + break; + case FOURCHAR('EOF\0'): + state.eof = chunk; + isParsing = FALSE; + break; + } + } +#endif + didLoadState = TRUE; + + cpu.setState(state.cpuState); + stic.setState(state.sticState); + psg.setState(state.psgState); + RAM8bit.setState(state.RAM8bitState, state.RAM8bitImage); + RAM16bit.setState(state.RAM16bitState, state.RAM16bitImage); + gram.setState(state.GRAMState, state.GRAMImage); + intellivoice.setState(state.ivoiceState); + ecs.setState(state.ecsState); + +close: + fclose(file); + file = NULL; +end: + return didLoadState; +} diff --git a/arm9/source/emucore/Intellivision.h b/arm9/source/emucore/Intellivision.h new file mode 100644 index 0000000..7ce85f7 --- /dev/null +++ b/arm9/source/emucore/Intellivision.h @@ -0,0 +1,52 @@ + +#ifndef INTELLIVISION_H +#define INTELLIVISION_H + +#include "Emulator.h" +#include "HandController.h" +#include "ECS.h" +#include "Intellivoice.h" +#include "Emulator.h" +#include "MemoryBus.h" +#include "RAM.h" +#include "ROM.h" +#include "CP1610.h" +#include "AY38900.h" +#include "AY38914.h" + +#define RAM8BIT_SIZE 0x00F0 +#define RAM16BIT_SIZE 0x0160 + +class Intellivision : public Emulator +{ + public: + Intellivision(); + + BOOL SaveState(const CHAR* filename); + BOOL LoadState(const CHAR* filename); + + private: + //core processors + CP1610 cpu; + AY38900 stic; + AY38914 psg; + + //core memories + RAM RAM8bit; + RAM RAM16bit; + ROM execROM; + ROM grom; + GRAM gram; + + //hand controllers + HandController player1Controller; + HandController player2Controller; + + //the ECS peripheral + ECS ecs; + + //the Intellivoice peripheral + Intellivoice intellivoice; +}; + +#endif diff --git a/arm9/source/emucore/Intellivoice.cpp b/arm9/source/emucore/Intellivoice.cpp new file mode 100644 index 0000000..a8b2dd5 --- /dev/null +++ b/arm9/source/emucore/Intellivoice.cpp @@ -0,0 +1,16 @@ + +#include "Intellivoice.h" + +IntellivoiceState Intellivoice::getState() +{ + IntellivoiceState state = {0}; + + state.sp0256State = sp0256.getState(); + + return state; +} + +void Intellivoice::setState(IntellivoiceState state) +{ + sp0256.setState(state.sp0256State); +} diff --git a/arm9/source/emucore/Intellivoice.h b/arm9/source/emucore/Intellivoice.h new file mode 100644 index 0000000..4718ea6 --- /dev/null +++ b/arm9/source/emucore/Intellivoice.h @@ -0,0 +1,40 @@ + +#ifndef INTELLIVOICE_H +#define INTELLIVOICE_H + +#include "Memory.h" +#include "Peripheral.h" +#include "types.h" +#include "Processor.h" +#include "SP0256.h" +#include "AudioOutputLine.h" + +TYPEDEF_STRUCT_PACK( _IntellivoiceState +{ + SP0256State sp0256State; +} IntellivoiceState; ) + +class Intellivoice : public Peripheral +{ +public: + Intellivoice() + : Peripheral("Intellivoice", "Intellivoice") + { + AddProcessor(&sp0256); + AddAudioProducer(&sp0256); + AddRAM(&sp0256.registers); + AddROM(&sp0256.ivoiceROM); + } + UINT32 getProcessorCount(); + void getProcessor(INT32 i, Processor** p); + UINT32 getMemoryCount(); + void getMemory(INT32 i, Memory** m); + + IntellivoiceState getState(); + void setState(IntellivoiceState state); + +private: + SP0256 sp0256; +}; + +#endif diff --git a/arm9/source/emucore/MOB.cpp b/arm9/source/emucore/MOB.cpp new file mode 100644 index 0000000..61d0a47 --- /dev/null +++ b/arm9/source/emucore/MOB.cpp @@ -0,0 +1,204 @@ + +#include "MOB.h" + +void MOB::reset() { + xLocation = 0; + yLocation = 0; + foregroundColor = 0; + cardNumber = 0; + collisionRegister = 0; + isGrom = FALSE; + isVisible = FALSE; + doubleWidth = FALSE; + doubleYResolution = FALSE; + doubleHeight = FALSE; + quadHeight = FALSE; + flagCollisions = FALSE; + horizontalMirror = FALSE; + verticalMirror = FALSE; + behindForeground = FALSE; + boundingRectangle.x = 0; + boundingRectangle.y = 0; + boundingRectangle.width = 0; + boundingRectangle.height = 0; + shapeChanged = TRUE; + boundsChanged = TRUE; + colorChanged = TRUE; +} + +void MOB::setXLocation(INT32 xLocation) { + if (xLocation == this->xLocation) + return; + + this->xLocation = xLocation; + boundsChanged = TRUE; +} + +void MOB::setYLocation(INT32 yLocation) { + if (yLocation == this->yLocation) + return; + + this->yLocation = yLocation; + boundsChanged = TRUE; +} + + +void MOB::setForegroundColor(INT32 foregroundColor) { + if (foregroundColor == this->foregroundColor) + return; + + this->foregroundColor = foregroundColor; + colorChanged = TRUE; +} + +void MOB::setCardNumber(INT32 cardNumber) { + if (cardNumber == this->cardNumber) + return; + + this->cardNumber = cardNumber; + shapeChanged = TRUE; +} + +void MOB::setVisible(BOOL isVisible) { + if (isVisible == this->isVisible) + return; + + this->isVisible = isVisible; +} + +void MOB::setDoubleWidth(BOOL doubleWidth) { + if (doubleWidth == this->doubleWidth) + return; + + this->doubleWidth = doubleWidth; + boundsChanged = TRUE; + shapeChanged = TRUE; +} + +void MOB::setDoubleYResolution(BOOL doubleYResolution) { + if (doubleYResolution == this->doubleYResolution) + return; + + this->doubleYResolution = doubleYResolution; + boundsChanged = TRUE; + shapeChanged = TRUE; +} + +void MOB::setDoubleHeight(BOOL doubleHeight) { + if (doubleHeight == this->doubleHeight) + return; + + this->doubleHeight = doubleHeight; + boundsChanged = TRUE; + shapeChanged = TRUE; +} + +void MOB::setQuadHeight(BOOL quadHeight) { + if (quadHeight == this->quadHeight) + return; + + this->quadHeight = quadHeight; + boundsChanged = TRUE; + shapeChanged = TRUE; +} + +void MOB::setFlagCollisions(BOOL flagCollisions) { + if (flagCollisions == this->flagCollisions) + return; + + this->flagCollisions = flagCollisions; +} + +void MOB::setHorizontalMirror(BOOL horizontalMirror) { + if (horizontalMirror == this->horizontalMirror) + return; + + this->horizontalMirror = horizontalMirror; + shapeChanged = TRUE; +} + +void MOB::setVerticalMirror(BOOL verticalMirror) { + if (verticalMirror == this->verticalMirror) + return; + + this->verticalMirror = verticalMirror; + shapeChanged = TRUE; +} + +void MOB::setBehindForeground(BOOL behindForeground) { + if (behindForeground == this->behindForeground) + return; + + this->behindForeground = behindForeground; +} + +void MOB::setGROM(BOOL grom) { + if (grom == this->isGrom) + return; + + this->isGrom = grom; + shapeChanged = TRUE; +} + +void MOB::markClean() { + shapeChanged = FALSE; + colorChanged = FALSE; +} + +MOBRect* MOB::getBounds() { + if (boundsChanged) { + boundingRectangle.x = xLocation-8; + boundingRectangle.y = yLocation-8; + boundingRectangle.width = 8 * (doubleWidth ? 2 : 1); + boundingRectangle.height = 4 * (quadHeight ? 4 : 1) * + (doubleHeight ? 2 : 1) * (doubleYResolution ? 2 : 1); + boundsChanged = FALSE; + } + return &boundingRectangle; +} + +MOBState MOB::getState() +{ + MOBState state = {0}; + + state.xLocation = this->xLocation;; + state.yLocation = this->yLocation;; + state.foregroundColor = this->foregroundColor; + state.cardNumber = this->cardNumber; + state.collisionRegister = this->collisionRegister; + state.isGrom = this->isGrom; + state.isVisible = this->isVisible; + state.doubleWidth = this->doubleWidth; + state.doubleYResolution = this->doubleYResolution; + state.doubleHeight = this->doubleHeight; + state.quadHeight = this->quadHeight; + state.flagCollisions = this->flagCollisions; + state.horizontalMirror = this->horizontalMirror; + state.verticalMirror = this->verticalMirror; + state.behindForeground = this->behindForeground; + + return state; +} + +void MOB::setState(MOBState state) +{ + this->xLocation = state.xLocation; + this->yLocation = state.yLocation; + this->foregroundColor = state.foregroundColor; + this->cardNumber = state.cardNumber; + this->collisionRegister = state.collisionRegister; + this->isGrom = state.isGrom; + this->isVisible = state.isVisible; + this->doubleWidth = state.doubleWidth; + this->doubleYResolution = state.doubleYResolution; + this->doubleHeight = state.doubleHeight; + this->quadHeight = state.quadHeight; + this->flagCollisions = state.flagCollisions; + this->horizontalMirror = state.horizontalMirror; + this->verticalMirror = state.verticalMirror; + this->behindForeground = state.behindForeground; + + this->boundsChanged = TRUE; + this->shapeChanged = TRUE; + this->colorChanged = TRUE; +} diff --git a/arm9/source/emucore/MOB.h b/arm9/source/emucore/MOB.h new file mode 100644 index 0000000..52661ea --- /dev/null +++ b/arm9/source/emucore/MOB.h @@ -0,0 +1,81 @@ + +#ifndef SPRITE_H +#define SPRITE_H + +#include "MOBRect.h" +#include "types.h" + +class AY38900; +class AY38900_Registers; + +TYPEDEF_STRUCT_PACK( _MOBState +{ + INT32 xLocation; + INT32 yLocation; + INT32 foregroundColor; + INT32 cardNumber; + UINT16 collisionRegister; + INT8 isGrom; + INT8 isVisible; + INT8 doubleWidth; + INT8 doubleYResolution; + INT8 doubleHeight; + INT8 quadHeight; + INT8 flagCollisions; + INT8 horizontalMirror; + INT8 verticalMirror; + INT8 behindForeground; +} MOBState; ) + +class MOB +{ + + friend class AY38900; + friend class AY38900_Registers; + + private: + MOB() {}; + void reset(); + void setXLocation(INT32 xLocation); + void setYLocation(INT32 yLocation); + void setForegroundColor(INT32 foregroundColor); + void setCardNumber(INT32 cardNumber); + void setVisible(BOOL isVisible); + void setDoubleWidth(BOOL doubleWidth); + void setDoubleYResolution(BOOL doubleYResolution); + void setDoubleHeight(BOOL doubleHeight); + void setQuadHeight(BOOL quadHeight); + void setFlagCollisions(BOOL flagCollisions); + void setHorizontalMirror(BOOL horizontalMirror); + void setVerticalMirror(BOOL verticalMirror); + void setBehindForeground(BOOL behindForeground); + void setGROM(BOOL grom); + void markClean(); + MOBRect* getBounds(); + + MOBState getState(); + void setState(MOBState state); + + INT32 xLocation; + INT32 yLocation; + INT32 foregroundColor; + INT32 cardNumber; + UINT16 collisionRegister; + BOOL isGrom; + BOOL isVisible; + BOOL doubleWidth; + BOOL doubleYResolution; + BOOL doubleHeight; + BOOL quadHeight; + BOOL flagCollisions; + BOOL horizontalMirror; + BOOL verticalMirror; + BOOL behindForeground; + BOOL boundsChanged; + BOOL shapeChanged; + BOOL colorChanged; + MOBRect boundingRectangle; + +}; + +#endif diff --git a/arm9/source/emucore/MOBRect.h b/arm9/source/emucore/MOBRect.h new file mode 100644 index 0000000..95065b0 --- /dev/null +++ b/arm9/source/emucore/MOBRect.h @@ -0,0 +1,25 @@ + +#ifndef RECTANGLE_H +#define RECTANGLE_H + +#include "types.h" + +class MOBRect +{ + + public: + BOOL intersects(MOBRect* r) { + return !((r->x + r->width <= x) || + (r->y + r->height <= y) || + (r->x >= x + width) || + (r->y >= y + height)); + } + + INT32 x; + INT32 y; + INT32 width; + INT32 height; + +}; + +#endif diff --git a/arm9/source/emucore/Memory.h b/arm9/source/emucore/Memory.h new file mode 100644 index 0000000..34358df --- /dev/null +++ b/arm9/source/emucore/Memory.h @@ -0,0 +1,26 @@ + +#ifndef MEMORY_H +#define MEMORY_H + +#include "types.h" + +class Memory +{ + + public: + virtual ~Memory() {} + + virtual void reset() = 0; + virtual UINT16 getReadSize() = 0; + virtual UINT16 getReadAddress() = 0; + virtual UINT16 getReadAddressMask() = 0; + virtual UINT16 peek(UINT16 location) = 0; + + virtual UINT16 getWriteSize() = 0; + virtual UINT16 getWriteAddress() = 0; + virtual UINT16 getWriteAddressMask() = 0; + virtual void poke(UINT16 location, UINT16 value) = 0; + +}; + +#endif diff --git a/arm9/source/emucore/MemoryBus.cpp b/arm9/source/emucore/MemoryBus.cpp new file mode 100644 index 0000000..e8ba6d2 --- /dev/null +++ b/arm9/source/emucore/MemoryBus.cpp @@ -0,0 +1,230 @@ +#include +#include "MemoryBus.h" + +MemoryBus::MemoryBus() +{ + UINT32 size = 1 << (sizeof(UINT16) << 3); + UINT64 i; + writeableMemoryCounts = new UINT8[size]; + memset(writeableMemoryCounts, 0, sizeof(UINT8) * size); + writeableMemorySpace = new Memory**[size]; + for (i = 0; i < size; i++) + writeableMemorySpace[i] = new Memory*[MAX_OVERLAPPED_MEMORIES]; + readableMemoryCounts = new UINT8[size]; + memset(readableMemoryCounts, 0, sizeof(UINT8) * size); + readableMemorySpace = new Memory**[size]; + for (i = 0; i < size; i++) + readableMemorySpace[i] = new Memory*[MAX_OVERLAPPED_MEMORIES]; + mappedMemoryCount = 0; +} + +MemoryBus::~MemoryBus() +{ + UINT64 size = 1 << (sizeof(UINT16) << 3); + UINT64 i; + delete[] writeableMemoryCounts; + for (i = 0; i < size; i++) + delete[] writeableMemorySpace[i]; + delete[] writeableMemorySpace; + delete[] readableMemoryCounts; + for (i = 0; i < size; i++) + delete[] readableMemorySpace[i]; + delete[] readableMemorySpace; +} + +void MemoryBus::reset() +{ + for (UINT8 i = 0; i < mappedMemoryCount; i++) + mappedMemories[i]->reset(); +} + +void MemoryBus::addMemory(Memory* m) +{ + UINT8 bitCount = sizeof(UINT16)<<3; + UINT8 bitShifts[sizeof(UINT16)<<3]; + UINT8 i; + + //get the important info + UINT16 readSize = m->getReadSize(); + UINT16 readAddress = m->getReadAddress(); + UINT16 readAddressMask = m->getReadAddressMask(); + UINT16 writeSize = m->getWriteSize(); + UINT16 writeAddress = m->getWriteAddress(); + UINT16 writeAddressMask = m->getWriteAddressMask(); + + //add all of the readable locations, if any + if (readAddressMask != 0) { + UINT8 zeroCount = 0; + for (i = 0; i < bitCount; i++) { + if (!(readAddressMask & (1<getReadSize(); + UINT16 readAddress = m->getReadAddress(); + UINT16 readAddressMask = m->getReadAddressMask(); + UINT16 writeSize = m->getWriteSize(); + UINT16 writeAddress = m->getWriteAddress(); + UINT16 writeAddressMask = m->getWriteAddressMask(); + + //add all of the readable locations, if any + if (readAddressMask != 0) { + UINT8 zeroCount = 0; + for (i = 0; i < bitCount; i++) { + if (!(readAddressMask & (1<peek(location); + return value; +} + +void MemoryBus::poke(UINT16 location, UINT16 value) +{ + UINT8 numMemories = writeableMemoryCounts[location]; + + for (UINT16 i = 0; i < numMemories; i++) + writeableMemorySpace[location][i]->poke(location, value); +} + diff --git a/arm9/source/emucore/MemoryBus.h b/arm9/source/emucore/MemoryBus.h new file mode 100644 index 0000000..8ddd8cf --- /dev/null +++ b/arm9/source/emucore/MemoryBus.h @@ -0,0 +1,46 @@ + +#ifndef MEMORYBUS_H +#define MEMORYBUS_H + +#include +#include +#include "Memory.h" + +#define MAX_MAPPED_MEMORIES 20 +#define MAX_OVERLAPPED_MEMORIES 3 + +/** + * Emulates a 64K memory bus which may be composed of 8-bit or 16-bit memory units. + * + * @author Kyle Davis + */ +extern UINT16 r[8]; + +class MemoryBus +{ + + public: + MemoryBus(); + virtual ~MemoryBus(); + + void reset(); + + UINT16 peek(UINT16 location); + inline UINT16 peek_fast(UINT16 location) {return readableMemorySpace[location][0]->peek(location);} + inline UINT16 peek_pc(void) {return readableMemorySpace[r[7]][0]->peek(r[7]);} + void poke(UINT16 location, UINT16 value); + + void addMemory(Memory* m); + void removeMemory(Memory* m); + void removeAll(); + + private: + Memory* mappedMemories[MAX_MAPPED_MEMORIES]; + UINT16 mappedMemoryCount; + UINT8* writeableMemoryCounts; + Memory*** writeableMemorySpace; + UINT8* readableMemoryCounts; + Memory*** readableMemorySpace; +}; + +#endif diff --git a/arm9/source/emucore/Palette.h b/arm9/source/emucore/Palette.h new file mode 100644 index 0000000..6f4549d --- /dev/null +++ b/arm9/source/emucore/Palette.h @@ -0,0 +1,14 @@ + +#ifndef PALETTE_H +#define PALETTE_H + +#include "types.h" + +typedef struct _PALETTE +{ + UINT16 m_iNumEntries; + UINT32 m_iEntries[256]; + +} PALETTE; + +#endif diff --git a/arm9/source/emucore/Peripheral.cpp b/arm9/source/emucore/Peripheral.cpp new file mode 100644 index 0000000..79b5bfc --- /dev/null +++ b/arm9/source/emucore/Peripheral.cpp @@ -0,0 +1,114 @@ + + +#include "Peripheral.h" + +void Peripheral::AddProcessor(Processor* p) +{ + processors[processorCount] = p; + processorCount++; +} + +void Peripheral::RemoveProcessor(Processor* p) +{ + for (UINT32 i = 0; i < processorCount; i++) { + if (processors[i] == p) { + for (UINT32 j = i; j < (processorCount-1); j++) + processors[j] = processors[j+1]; + processorCount--; + return; + } + } +} + +UINT16 Peripheral::GetProcessorCount() +{ + return processorCount; +} + +Processor* Peripheral::GetProcessor(UINT16 i) +{ + return processors[i]; +} + +void Peripheral::AddRAM(RAM* m) +{ + rams[ramCount] = m; + ramCount++; +} + +UINT16 Peripheral::GetRAMCount() +{ + return ramCount; +} + +RAM* Peripheral::GetRAM(UINT16 i) +{ + return rams[i]; +} + +void Peripheral::AddVideoProducer(VideoProducer* vp) +{ + videoProducers[videoProducerCount] = vp; + videoProducerCount++; +} + +UINT16 Peripheral::GetVideoProducerCount() +{ + return videoProducerCount; +} + +VideoProducer* Peripheral::GetVideoProducer(UINT16 i) +{ + return videoProducers[i]; +} + +void Peripheral::AddAudioProducer(AudioProducer* ap) +{ + audioProducers[audioProducerCount] = ap; + audioProducerCount++; +} + +UINT16 Peripheral::GetAudioProducerCount() +{ + return audioProducerCount; +} + +AudioProducer* Peripheral::GetAudioProducer(UINT16 i) +{ + return audioProducers[i]; +} + +void Peripheral::AddInputConsumer(InputConsumer* ic) +{ + inputConsumers[inputConsumerCount] = ic; + inputConsumerCount++; +} + +UINT16 Peripheral::GetInputConsumerCount() +{ + return inputConsumerCount; +} + +InputConsumer* Peripheral::GetInputConsumer(UINT16 i) +{ + return inputConsumers[i]; +} + +void Peripheral::AddROM(ROM* r) +{ + printf("ROM Added [%s]\n", r->getName()); + roms[romCount] = r; + romCount++; +} + +UINT16 Peripheral::GetROMCount() +{ + return romCount; +} + +ROM* Peripheral::GetROM(UINT16 i) +{ + return roms[i]; +} + + diff --git a/arm9/source/emucore/Peripheral.h b/arm9/source/emucore/Peripheral.h new file mode 100644 index 0000000..8a90382 --- /dev/null +++ b/arm9/source/emucore/Peripheral.h @@ -0,0 +1,236 @@ + +#ifndef PERIPHERAL_H +#define PERIPHERAL_H + +#include "Processor.h" +#include "RAM.h" +#include "ROM.h" +#include "AudioProducer.h" +#include "VideoProducer.h" +#include "InputConsumer.h" + +#define MAX_COMPONENTS 16 + +/** + * A peripheral represents a device that contains emulated hardware components, including + * processors and memory units. Each processor or memory unit may optionally also be a + * video producer, audio producer, and/or input consumer. However, some input consumers are + * neither processors or memory units. + */ +class Peripheral +{ + public: + /** + * Gets the name of this peripheral. + * + * @return the peripheral name + */ + const char* GetName() { return peripheralName; } + + /** + * Gets the short name of this peripheral. + * + * @return the short peripheral name + */ + const char* GetShortName() { return peripheralShortName; } + + /** + * Adds a processor to this peripheral. + * + * @param processor the processor to add + */ + void AddProcessor(Processor* processor); + + /** + * Removes a processor from this peripheral. + * + * @param processor the processor to remove + */ + void RemoveProcessor(Processor* processor); + + /** + * Gets the number of processors in this peripheral. + * + * @return the number of processors + */ + UINT16 GetProcessorCount(); + + /** + * Gets a pointer to the processor indicated by an index. + * + * @param index the index of the processor to return + * @return a pointer to the processor + */ + Processor* GetProcessor(UINT16 index); + + /** + * Adds a RAM unit to this peripheral. + * + * @param ram the RAM unit to add + */ + void AddRAM(RAM* ram); + + /** + * Gets the number of RAM units in this peripheral. + * + * @return the number of RAM units + */ + UINT16 GetRAMCount(); + + /** + * Gets a pointer to the RAM unit indicated by an index. + * + * @param index the index of the RAM unit to return + * @return a pointer to the RAM unit + */ + RAM* GetRAM(UINT16 index); + + /** + * Adds a ROM unit to this peripheral. + * + * @param rom the ROM unit to add + */ + void AddROM(ROM* rom); + + /** + * Gets the number of ROM units in this peripheral. + * + * @return the number of ROM units + */ + UINT16 GetROMCount(); + + /** + * Gets a pointer to the ROM unit indicated by an index. + * + * @param index the index of the ROM unit to return + * @return a pointer to the ROM unit + */ + ROM* GetROM(UINT16 index); + + /** + * Adds a video producer to this peripheral. + * + * @param vp the video producer to add + */ + void AddVideoProducer(VideoProducer* vp); + + /** + * Gets the number of video producers in this peripheral. + * + * @return the number of video producers + */ + UINT16 GetVideoProducerCount(); + + /** + * Gets a pointer to the video producer indicated by an index. + * + * @param index the index of the video producer to return + * @return a pointer to the video producer + */ + VideoProducer* GetVideoProducer(UINT16 index); + + /** + * Adds an audio producer to this peripheral. + * + * @param ap the audio producer to add + */ + void AddAudioProducer(AudioProducer* ap); + + /** + * Gets the number of audio producers in this peripheral. + * + * @return the number of audio producers + */ + UINT16 GetAudioProducerCount(); + + /** + * Gets a pointer to the audio producer indicated by an index. + * + * @param index the index of the audio producer to return + * @return a pointer to the audio producer + */ + AudioProducer* GetAudioProducer(UINT16 index); + + /** + * Adds an input consumer to this peripheral. + * + * @param ic the input consumer to add + */ + void AddInputConsumer(InputConsumer*); + + /** + * Gets the number of input consumers in this peripheral. + * + * @return the number of input consumers + */ + UINT16 GetInputConsumerCount(); + + /** + * Gets a pointer to the input consumer indicated by an index. + * + * @param index the index of the input consumer to return + * @return a pointer to the input consumer + */ + InputConsumer* GetInputConsumer(UINT16 index); + + protected: + /** + * Constructs a peripheral. + * + * @param name the name of the peripheral + * @param shortName the short name of the peripheral + */ + Peripheral(const CHAR* name, const CHAR* shortName) + : processorCount(0), + videoProducerCount(0), + audioProducerCount(0), + inputConsumerCount(0), + ramCount(0), + romCount(0), + peripheralName(NULL), + peripheralShortName(NULL) + { + if (name) { + peripheralName = new CHAR[strlen(name)+1]; + strcpy(peripheralName, name); + } + if (shortName) { + peripheralShortName = new CHAR[strlen(shortName)+1]; + strcpy(peripheralShortName, shortName); + } + } + + /** + * Destroys the peripheral. + */ + ~Peripheral() + { + delete[] peripheralShortName; + delete[] peripheralName; + } + + /** + * The peripheral name. The Rip class exposes the periphal name as mutable, so we leave + * it exposed as protected access here. This will probably change in the future. + */ + char* peripheralName; + + private: + char* peripheralShortName; + Processor* processors[MAX_COMPONENTS]; + UINT16 processorCount; + VideoProducer* videoProducers[MAX_COMPONENTS]; + UINT16 videoProducerCount; + AudioProducer* audioProducers[MAX_COMPONENTS]; + UINT16 audioProducerCount; + InputConsumer* inputConsumers[MAX_COMPONENTS]; + UINT16 inputConsumerCount; + RAM* rams[MAX_COMPONENTS]; + UINT16 ramCount; + ROM* roms[MAX_COMPONENTS]; + UINT16 romCount; + +}; + +#endif + diff --git a/arm9/source/emucore/Processor.cpp b/arm9/source/emucore/Processor.cpp new file mode 100644 index 0000000..2405ff7 --- /dev/null +++ b/arm9/source/emucore/Processor.cpp @@ -0,0 +1,49 @@ + +#include "CP1610.h" + +SignalLine nullPin; + +Processor::Processor(const char* nm) + : name(nm) +{ + for (UINT8 i = 0; i < MAX_PINS; i++) { + pinOut[i] = &nullPin; + pinIn[i] = &nullPin; + } +} + +Processor::~Processor() +{ + for (UINT8 i = 0; i < MAX_PINS; i++) { + if (pinOut[i] != &nullPin) + delete pinOut[i]; + } +} + +void Processor::connectPinOut(UINT8 pinOutNum, Processor* targetProcessor, + UINT8 targetPinInNum) +{ + if (pinOut[pinOutNum] != &nullPin) + disconnectPinOut(pinOutNum); + + if (targetProcessor->pinIn[targetPinInNum] != &nullPin) { + targetProcessor->pinIn[targetPinInNum]->pinOutProcessor->disconnectPinOut( + targetProcessor->pinIn[targetPinInNum]->pinOutNum); + } + + pinOut[pinOutNum] = new SignalLine(this, pinOutNum, targetProcessor, + targetPinInNum); + + targetProcessor->pinIn[targetPinInNum] = pinOut[pinOutNum]; +} + +void Processor::disconnectPinOut(UINT8 pinOutNum) +{ + if (pinOut[pinOutNum] == &nullPin) + return; + + SignalLine* s = pinOut[pinOutNum]; + s->pinInProcessor->pinIn[s->pinInNum] = &nullPin; + pinOut[pinOutNum] = &nullPin; + delete s; +} diff --git a/arm9/source/emucore/Processor.h b/arm9/source/emucore/Processor.h new file mode 100644 index 0000000..cfc9c76 --- /dev/null +++ b/arm9/source/emucore/Processor.h @@ -0,0 +1,61 @@ + +#ifndef PROCESSOR_H +#define PROCESSOR_H + +#include "SignalLine.h" +#include "types.h" +#include "MemoryBus.h" +#define MAX_PINS 16 + +class ProcessorBus; +class ScheduleQueue; + +/** + * An abstract class representing a processor, which is a hardware component + * that requires a clock input. + */ +class Processor +{ + + friend class ProcessorBus; + + public: + virtual ~Processor(); + + const char* getName() { return name; } + + virtual void resetProcessor() = 0; + + /** + * Describes the clock speed of this processor. Unlike many emulation + * codebases, this codebase allows a processor to run at any speed + * it prefers, and the ProcessorBus is responsible for scheduling + * execution of each processor based on all of the various processor + * speeds. + */ + virtual INT32 getClockSpeed() = 0; + + /** + * + */ + virtual INT32 tick(INT32 i) = 0; + + virtual void connectPinOut(UINT8 pinOutNum, Processor* targetProcessor, + UINT8 targetPinInNum); + virtual void disconnectPinOut(UINT8 pinOutNum); + + virtual BOOL isIdle() { return FALSE; }; + + protected: + Processor(const char* name); + + const char* name; + SignalLine* pinIn[MAX_PINS]; + SignalLine* pinOut[MAX_PINS]; + + ProcessorBus* processorBus; + ScheduleQueue* scheduleQueue; + +}; + +#endif diff --git a/arm9/source/emucore/ProcessorBus.cpp b/arm9/source/emucore/ProcessorBus.cpp new file mode 100644 index 0000000..98d28a8 --- /dev/null +++ b/arm9/source/emucore/ProcessorBus.cpp @@ -0,0 +1,318 @@ + +#include "ProcessorBus.h" + +ProcessorBus::ProcessorBus() +: processorCount(0), + startQueue(NULL), + endQueue(NULL) +{} + +ProcessorBus::~ProcessorBus() +{ + for (UINT32 i = 0; i < processorCount; i++) { + if (processors[i]->scheduleQueue) + delete processors[i]->scheduleQueue; + } +} + +void ProcessorBus::addProcessor(Processor* p) +{ + processors[processorCount] = p; + processorCount++; + p->processorBus = this; + p->scheduleQueue = new ScheduleQueue(p); +} + +void ProcessorBus::removeProcessor(Processor* p) +{ + for (UINT32 i = 0; i < processorCount; i++) { + if (processors[i] == p) { + for (UINT32 j = i; j < (processorCount-1); j++) + processors[j] = processors[j+1]; + processorCount--; + delete p->scheduleQueue; + p->scheduleQueue = NULL; + return; + } + } +} + +void ProcessorBus::removeAll() +{ + while (processorCount) + removeProcessor(processors[0]); +} + + +void ProcessorBus::reset() +{ + if (processorCount == 0) + return; + + UINT64 totalClockSpeed = 1; + startQueue = endQueue = NULL; + + //reorder the processor queue so that it is in the natural (starting) order + for (UINT32 i = 0; i < processorCount; i++) { + Processor* p = processors[i]; + totalClockSpeed = lcm(totalClockSpeed, ((UINT64)p->getClockSpeed())); + + ScheduleQueue* nextQueue = p->scheduleQueue; + nextQueue->tick = 0; + if (startQueue == NULL) { + startQueue = nextQueue; + nextQueue->previous = NULL; + nextQueue->next = NULL; + } + else { + endQueue->next = nextQueue; + nextQueue->previous = endQueue; + nextQueue->next = NULL; + } + endQueue = nextQueue; + } + + //pre-cache the multiplication factor required to convert each processor's clock speed to + //the common clock speed, and reset each processor + for (UINT32 i = 0; i < processorCount; i++) { + Processor* p = processors[i]; + p->scheduleQueue->tickFactor = (totalClockSpeed / ((UINT64)p->getClockSpeed())); + p->resetProcessor(); + } +} + +void ProcessorBus::run() +{ + running = true; + while (running) { + // TODO: jeremiah sypult, saw crash when NULL + if (startQueue->next == NULL) { + break; + } + //tick the processor that is at the head of the queue + int minTicks = (int)((startQueue->next->tick / startQueue->tickFactor) + 1); + startQueue->tick = ((UINT64)startQueue->processor->tick(minTicks)) * startQueue->tickFactor; + + //now reschedule the processor for later processing + ScheduleQueue* tmp1 = startQueue; + while (tmp1->next != NULL && startQueue->tick > tmp1->next->tick) { + startQueue->tick -= tmp1->next->tick; + tmp1 = tmp1->next; + } + + //reorganize the scheduling queue + ScheduleQueue* queueToShuffle = startQueue; + startQueue = startQueue->next; + queueToShuffle->previous = tmp1; + queueToShuffle->next = tmp1->next; + tmp1->next = queueToShuffle; + if (queueToShuffle->next != NULL) { + queueToShuffle->next->tick -= queueToShuffle->tick; + queueToShuffle->next->previous = queueToShuffle; + } + else + endQueue = queueToShuffle; + } +} + +void ProcessorBus::stop() +{ + running = false; +} + +void ProcessorBus::halt(Processor*) +{ +} + +void ProcessorBus::unhalt(Processor*) +{ +} + +void ProcessorBus::pause(Processor* p, int ticks) +{ + ScheduleQueue* q = p->scheduleQueue; + UINT64 commonTicks = ((UINT64)ticks) * q->tickFactor; + if (q->next == NULL) + q->tick += commonTicks; + else if (commonTicks <= q->next->tick) { + q->tick += commonTicks; + q->next->tick -= commonTicks; + } + else { + //pull this processor out of the scheduling stream and reschedule it + //add my current ticks to the next guy's delta ticks + q->next->tick += q->tick; + //add the common ticks to my ticks + q->tick += commonTicks; + //have the previous q point to the next q + if (q->previous != NULL) { + q->previous->next = q->next; + q->next->previous = q->previous; + } + reschedule(p->scheduleQueue); + } +} + +void ProcessorBus::reschedule(ScheduleQueue* queueToShuffle) +{ + //look for where to put this processor in the scheduling queue + ScheduleQueue* tmp1 = queueToShuffle; + while (tmp1->next != NULL && queueToShuffle->tick > tmp1->next->tick) { + queueToShuffle->tick -= tmp1->next->tick; + tmp1 = tmp1->next; + } + + //reorganize the scheduling queue + if (queueToShuffle == startQueue) + startQueue = queueToShuffle->next; + queueToShuffle->previous = tmp1; + queueToShuffle->next = tmp1->next; + tmp1->next = queueToShuffle; + if (queueToShuffle->next != NULL) { + queueToShuffle->next->tick -= queueToShuffle->tick; + queueToShuffle->next->previous = queueToShuffle; + } + else + endQueue = queueToShuffle; +} + +UINT64 lcm(UINT64 a, UINT64 b) { + //This is an implementation of Euclid's Algorithm for determining + //the greatest common denominator (gcd) of two numbers. + UINT64 m = a; + UINT64 n = b; + UINT64 r = m % n; + while (r != 0) { + m = n; + n = r; + r = m % n; + } + UINT64 gcd = n; + + //lcm is determined from gcd through the following equation + return (a/gcd)*b; +} + +/* +ProcessorBus::ProcessorBus() +{ + processorDebugOn = FALSE; + processorCount = 0; + memset(processors, 0, sizeof(Processor*) * MAX_PROCESSORS); + memset(processorTickFactors, 0, sizeof(INT64) * MAX_PROCESSORS); + memset(processorTicks, 0, sizeof(INT64) * MAX_PROCESSORS); + memset(processorsIdle, 0, sizeof(BOOL) * MAX_PROCESSORS); +} + +void ProcessorBus::setAudioMixer(AudioMixer* am) +{ + this->audioMixer = am; +} + +void ProcessorBus::init() +{ + UINT64 totalClockSpeed = 1; + UINT32 i; + for (i = 0; i < processorCount; i++) { + totalClockSpeed = lcm(totalClockSpeed, + ((UINT64)processors[i]->getClockSpeed())); + } + + for (i = 0; i < processorCount; i++) { + processorsIdle[i] = FALSE; + processorTicks[i] = 0; + processorTickFactors[i] = (totalClockSpeed / + ((UINT64)processors[i]->getClockSpeed())); + + processors[i]->initProcessor(); + } +} + +void ProcessorBus::release() +{ + for (UINT32 i = 0; i < processorCount; i++) + processors[i]->releaseProcessor(); +} + +void ProcessorBus::addProcessor(Processor* p) +{ + processors[processorCount] = p; + processorCount++; +} + +void ProcessorBus::removeProcessor(Processor* p) +{ + for (UINT32 i = 0; i < processorCount; i++) { + if (processors[i] == p) { + for (UINT32 j = i; j < (processorCount-1); j++) + processors[j] = processors[j+1]; + processorCount--; + return; + } + } +} + +void ProcessorBus::removeAll() +{ + processorCount = 0; +} + +void ProcessorBus::tick() +{ + //determine the next tick delta +#ifdef _MSC_VER + UINT64 tickDelta = 0xFFFFFFFFFFFFFFFF; +#else + UINT64 tickDelta = 0xFFFFFFFFFFFFFFFFll; +#endif + + UINT32 i; + for (i = 0; i < processorCount; i++) { + //wake up any processors that want to wake up + processorsIdle[i] = (processorsIdle[i] && processors[i]->isIdle()); + + //if the processor is not idle by this point, use it + //to calculate the tick delta + if (!processorsIdle[i] && (UINT64)processorTicks[i] < tickDelta) + tickDelta = processorTicks[i]; + } + + //move the audio mixer clock forward by the tick delta amount + audioMixer->clock += tickDelta; + + for (i = 0; i < processorCount; i++) { + //skip this processor if it has been idled + if (!processorsIdle[i]) { + processorTicks[i] -= tickDelta; + + //if the clock just caught up to the processor, and + //if the processor is not about to go idle, then run it + if (processorTicks[i] == 0) { + if (processors[i]->isIdle()) + processorsIdle[i] = TRUE; + else { + processorTicks[i] = (((UINT64)processors[i]->tick()) * + processorTickFactors[i]); + } + } + } + } +} + +UINT64 lcm(UINT64 a, UINT64 b) { + //This is an implementation of Euclid's Algorithm for determining + //the greatest common denominator (gcd) of two numbers. + UINT64 m = a; + UINT64 n = b; + UINT64 r = m % n; + while (r != 0) { + m = n; + n = r; + r = m % n; + } + UINT64 gcd = n; + + //lcm is determined from gcd through the following equation + return (a/gcd)*b; +} +*/ \ No newline at end of file diff --git a/arm9/source/emucore/ProcessorBus.h b/arm9/source/emucore/ProcessorBus.h new file mode 100644 index 0000000..3ab5d96 --- /dev/null +++ b/arm9/source/emucore/ProcessorBus.h @@ -0,0 +1,65 @@ + +#ifndef PROCESSORBUS_H +#define PROCESSORBUS_H + +#include "Processor.h" +#include "types.h" +#include "AudioMixer.h" + +const INT32 MAX_PROCESSORS = 15; + +class ScheduleQueue; + +class ProcessorBus +{ + +public: + ProcessorBus(); + virtual ~ProcessorBus(); + void addProcessor(Processor* p); + void removeProcessor(Processor* p); + void removeAll(); + + void reset(); + void run(); + void stop(); + + void halt(Processor* p); + void unhalt(Processor* p); + void pause(Processor* p, int ticks); + +private: + void reschedule(ScheduleQueue*); + + UINT32 processorCount; + Processor* processors[MAX_PROCESSORS]; + bool running; + ScheduleQueue* startQueue; + ScheduleQueue* endQueue; + +}; + +class ScheduleQueue +{ + friend class ProcessorBus; + +private: + ScheduleQueue(Processor* p) + : tickFactor(0), + tick(0), + previous(NULL), + next(NULL) + { + this->processor = p; + } + + Processor* processor; + UINT64 tickFactor; + UINT64 tick; + ScheduleQueue* previous; + ScheduleQueue* next; +}; + +UINT64 lcm(UINT64 a, UINT64 b); + +#endif diff --git a/arm9/source/emucore/RAM.ITCM.cpp b/arm9/source/emucore/RAM.ITCM.cpp new file mode 100644 index 0000000..4b72a6e --- /dev/null +++ b/arm9/source/emucore/RAM.ITCM.cpp @@ -0,0 +1,157 @@ +#include +#include "RAM.h" + +UINT16 fast_ram[4096] __attribute__((section(".dtcm"))); +UINT16 fast_ram_idx = 0; + +RAM::RAM(UINT16 size, UINT16 location) +: enabled(TRUE) +{ + this->size = size; + this->location = location; + this->readAddressMask = 0xFFFF; + this->writeAddressMask = 0xFFFF; + this->bitWidth = sizeof(UINT16)<<3; + this->trimmer = (UINT16)((1 << (sizeof(UINT16) << 3)) - 1); + //image = new UINT16[size]; + image = &fast_ram[fast_ram_idx]; + fast_ram_idx += size; +} + +RAM::RAM(UINT16 size, UINT16 location, UINT8 bitWidth) +: enabled(TRUE) +{ + this->size = size; + this->location = location; + this->readAddressMask = 0xFFFF; + this->writeAddressMask = 0xFFFF; + this->bitWidth = bitWidth; + this->trimmer = (UINT16)((1 << bitWidth) - 1); + //image = new UINT16[size]; + image = &fast_ram[fast_ram_idx]; + fast_ram_idx += size; +} + +RAM::RAM(UINT16 size, UINT16 location, UINT16 readAddressMask, UINT16 writeAddressMask) +: enabled(TRUE) +{ + this->size = size; + this->location = location; + this->readAddressMask = readAddressMask; + this->writeAddressMask = writeAddressMask; + this->bitWidth = sizeof(UINT16)<<3; + this->trimmer = (UINT16)((1 << bitWidth) - 1); + //image = new UINT16[size]; + image = &fast_ram[fast_ram_idx]; + fast_ram_idx += size; +} + +RAM::RAM(UINT16 size, UINT16 location, UINT16 readAddressMask, UINT16 writeAddressMask, UINT8 bitWidth) +: enabled(TRUE) +{ + this->size = size; + this->location = location; + this->readAddressMask = readAddressMask; + this->writeAddressMask = writeAddressMask; + this->bitWidth = bitWidth; + this->trimmer = (UINT16)((1 << bitWidth) - 1); + //image = new UINT16[size]; + image = &fast_ram[fast_ram_idx]; + fast_ram_idx += size; +} + +RAM::~RAM() +{ + //delete[] image; +} + +void RAM::reset() +{ + enabled = TRUE; + for (UINT16 i = 0; i < size; i++) + image[i] = 0; +} + +void RAM::SetEnabled(BOOL b) +{ + enabled = b; +} + +UINT8 RAM::getBitWidth() +{ + return bitWidth; +} + +UINT16 RAM::getReadSize() +{ + return size; +} + +UINT16 RAM::getReadAddress() +{ + return location; +} + +UINT16 RAM::getReadAddressMask() +{ + return readAddressMask; +} + +UINT16 RAM::getWriteSize() +{ + return size; +} + +UINT16 RAM::getWriteAddress() +{ + return location; +} + +UINT16 RAM::getWriteAddressMask() +{ + return writeAddressMask; +} + +UINT16 RAM::peek(UINT16 location) +{ + return image[(location&readAddressMask)-this->location]; +} + +void RAM::poke(UINT16 location, UINT16 value) +{ + image[(location&writeAddressMask)-this->location] = (value & trimmer); +} + +RAMState RAM::getState(UINT16* image) +{ + RAMState state = {0}; + + state.enabled = this->enabled; + state.size = this->size; + state.location = this->location; + state.readAddressMask = this->readAddressMask; + state.writeAddressMask = this->writeAddressMask; + state.bitWidth = this->bitWidth; + state.trimmer = this->trimmer; + + if (image != NULL) { + this->getImage(image, 0, this->getImageByteSize()); + } + + return state; +} + +void RAM::setState(RAMState state, UINT16* image) +{ + this->enabled = state.enabled; + this->size = state.size; + this->location = state.location; + this->readAddressMask = state.readAddressMask; + this->writeAddressMask = state.writeAddressMask; + this->bitWidth = state.bitWidth; + this->trimmer = state.trimmer; + + if (image != NULL) { + this->setImage(image, 0, this->getImageByteSize()); + } +} diff --git a/arm9/source/emucore/RAM.h b/arm9/source/emucore/RAM.h new file mode 100644 index 0000000..77bdc21 --- /dev/null +++ b/arm9/source/emucore/RAM.h @@ -0,0 +1,72 @@ + +#ifndef RAM_H +#define RAM_H + +#include +#include "Memory.h" + +TYPEDEF_STRUCT_PACK( _RAMState +{ + INT8 enabled; + UINT8 bitWidth; + UINT16 size; + UINT16 location; + UINT16 readAddressMask; + UINT16 writeAddressMask; + UINT16 trimmer; +} RAMState; ) + +class RAM : public Memory +{ + + public: + RAM(UINT16 size, UINT16 location); + RAM(UINT16 size, UINT16 location, UINT8 bitWidth); + RAM(UINT16 size, UINT16 location, UINT16 readAddressMask, UINT16 writeAddressMask); + RAM(UINT16 size, UINT16 location, UINT16 readAddressMask, UINT16 writeAddressMask, UINT8 bitWidth); + virtual ~RAM(); + + virtual void reset(); + UINT8 getBitWidth(); + + UINT16 getReadSize(); + UINT16 getReadAddress(); + UINT16 getReadAddressMask(); + virtual UINT16 peek(UINT16 location); + + UINT16 getWriteSize(); + UINT16 getWriteAddress(); + UINT16 getWriteAddressMask(); + virtual void poke(UINT16 location, UINT16 value); + + inline size_t getImageByteSize() { + return size * sizeof(UINT16); + } + void getImage(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, image + offset, size); + } + void setImage(void* src, UINT16 offset, UINT16 size) { + memcpy(image + offset, src, size); + } + + RAMState getState(UINT16* image); + void setState(RAMState state, UINT16* image); + + //enabled attributes + void SetEnabled(BOOL b); + BOOL IsEnabled() { return enabled; } + + protected: + BOOL enabled; + UINT16 size; + UINT16 location; + UINT16 readAddressMask; + UINT16 writeAddressMask; + + private: + UINT8 bitWidth; + UINT16 trimmer; + UINT16* image; +}; + +#endif diff --git a/arm9/source/emucore/ROM.ITCM.cpp b/arm9/source/emucore/ROM.ITCM.cpp new file mode 100644 index 0000000..2de3911 --- /dev/null +++ b/arm9/source/emucore/ROM.ITCM.cpp @@ -0,0 +1,174 @@ +#include +#include +#include "ROM.h" + +ROM::ROM(const CHAR* n, const CHAR* f, UINT32 o, UINT8 byteWidth, UINT16 size, UINT16 location, BOOL i) +: enabled(TRUE), + loaded(FALSE), + internal(i) +{ + Initialize(n, f, o, byteWidth, size, location, 0xFFFF); +} + +ROM::ROM(const CHAR* n, void* image, UINT8 byteWidth, UINT16 size, UINT16 location, UINT16 readAddressMask) +: enabled(TRUE), + loaded(TRUE), + internal(FALSE) +{ + Initialize(n, "", 0, byteWidth, size, location, readAddressMask); + memcpy(this->image, image, size*byteWidth); +} + +void ROM::Initialize(const CHAR* n, const CHAR* f, UINT32 o, UINT8 byteWidth, UINT16 size, UINT16 location, UINT16 readMask) +{ + name = new CHAR[strlen(n)+1]; + strcpy(name, n); + filename = new CHAR[strlen(f)+1]; + strcpy(filename, f); + fileoffset = o; + this->byteWidth = byteWidth; + this->multiByte = (byteWidth > 1) ? 1:0; + this->size = size; + this->location = location; + this->readAddressMask = readMask; + this->image = new UINT8[size*byteWidth]; + peekFunc = (byteWidth == 1 ? &ROM::peek1 : (byteWidth == 2 ? &ROM::peek2 : + (byteWidth == 4 ? &ROM::peek4 : &ROM::peekN))); +} + +ROM::~ROM() +{ + if (name) + delete[] name; + if (filename) + delete[] filename; + if (image) + delete[] image; +} + +BOOL ROM::load(const char* filename) +{ + return load(filename, 0); +} + +BOOL ROM::load(const char* filename, UINT32 offset) +{ + FILE* f = fopen(filename, "rb"); + if (!f) + return FALSE; + + fseek(f, offset, SEEK_SET); + int byteCount = size*byteWidth; + int totalSize = 0; + while (totalSize < byteCount) { + for (UINT8 k = 0; k < byteWidth; k++) + ((UINT8*)image)[totalSize+(byteWidth-k-1)] = (UINT8)fgetc(f); + totalSize += byteWidth; + } + fclose(f); + loaded = TRUE; + + if (loaded) printf("Successful Load of [%s]\n", filename); + else printf("Unsuccessful Load of [%s]\n", filename); + + return TRUE; +} + +BOOL ROM::load(void* buffer) +{ + UINT8* byteImage = (UINT8*)buffer; + int byteCount = size*byteWidth; + int totalSize = 0; + while (totalSize < byteCount) { + for (UINT8 k = 0; k < byteWidth; k++) + ((UINT8*)image)[totalSize+(byteWidth-k-1)] = byteImage[totalSize+k]; + totalSize += byteWidth; + } + + loaded = TRUE; + return TRUE; +} + +void ROM::SetEnabled(BOOL b) +{ + enabled = b; + if (enabled) + peekFunc = (byteWidth == 1 ? &ROM::peek1 : (byteWidth == 2 ? &ROM::peek2 : + (byteWidth == 4 ? &ROM::peek4 : &ROM::peekN))); + else + peekFunc = &ROM::peekN; +} + +const CHAR* ROM::getName() +{ + return name; +} + +const CHAR* ROM::getDefaultFileName() +{ + return filename; +} + +UINT32 ROM::getDefaultFileOffset() +{ + return fileoffset; +} + +UINT16 ROM::getReadSize() +{ + return size; +} + +UINT16 ROM::getReadAddress() { + return location; +} + +UINT16 ROM::getReadAddressMask() +{ + return readAddressMask; +} + +UINT16 ROM::getWriteSize() +{ + return 0; +} + +UINT16 ROM::getWriteAddress() { + return 0; +} + +UINT16 ROM::getWriteAddressMask() +{ + return 0; +} + +UINT8 ROM::getByteWidth() +{ + return byteWidth; +} + +UINT16 ROM::peek1(UINT16 location) +{ + return ((UINT8*)image)[(location&readAddressMask)-this->location]; +} + +UINT16 ROM::peek2(UINT16 location) +{ + return ((UINT16*)image)[(location&readAddressMask)-this->location]; +} + +UINT16 ROM::peek4(UINT16 location) +{ + return (UINT16)((UINT32*)image)[(location&readAddressMask)-this->location]; +} + +UINT16 ROM::peekN(UINT16) +{ + return 0xFFFF; +} + +void ROM::poke(UINT16, UINT16) +{ + //can't change ROM +} + diff --git a/arm9/source/emucore/ROM.h b/arm9/source/emucore/ROM.h new file mode 100644 index 0000000..df9c84d --- /dev/null +++ b/arm9/source/emucore/ROM.h @@ -0,0 +1,73 @@ + +#ifndef ROM_H +#define ROM_H + +#include "Memory.h" +#include "types.h" + +class ROM : public Memory +{ + +public: + ROM(const CHAR* name, const CHAR* defaultFilename, UINT32 defaultFileOffset, UINT8 byteWidth, UINT16 size, UINT16 location, BOOL internal = FALSE); + ROM(const CHAR* name, void* image, UINT8 byteWidth, UINT16 size, UINT16 location, UINT16 readAddressMask = 0xFFFF); + virtual ~ROM(); + + const CHAR* getName(); + const CHAR* getDefaultFileName(); + UINT32 getDefaultFileOffset(); + BOOL load(const CHAR* filename); + BOOL load(const CHAR* filename, UINT32 offset); + BOOL load(void* image); + BOOL isLoaded() { return loaded; } + BOOL isInternal() { return internal; } + + //enabled attributes + void SetEnabled(BOOL b); + BOOL IsEnabled() { return enabled; } + + //functions to implement the Memory interface + virtual void reset() {} + UINT8 getByteWidth(); + + UINT16 getReadSize(); + UINT16 getReadAddress(); + UINT16 getReadAddressMask(); + inline virtual UINT16 peek(UINT16 location) + { + if (multiByte) return ((UINT16*)image)[(location)-this->location]; + return ((UINT8*)image)[(location)-this->location]; + // return (*this.*peekFunc)(location); + } + + UINT16 getWriteSize(); + UINT16 getWriteAddress(); + UINT16 getWriteAddressMask(); + virtual void poke(UINT16 location, UINT16 value); + +private: + void Initialize(const CHAR* n, const CHAR* f, UINT32 o, UINT8 byteWidth, UINT16 size, UINT16 location, UINT16 readMask); + + UINT16 (ROM::*peekFunc)(UINT16); + UINT16 peek1(UINT16 location); + UINT16 peek2(UINT16 location); + UINT16 peek4(UINT16 location); + UINT16 peekN(UINT16 location); + + CHAR* name; + CHAR* filename; + UINT32 fileoffset; + + UINT8* image; + UINT8 byteWidth; + UINT8 multiByte; + BOOL enabled; + UINT16 size; + UINT16 location; + UINT16 readAddressMask; + BOOL loaded; + BOOL internal; + +}; + +#endif diff --git a/arm9/source/emucore/ROMBanker.cpp b/arm9/source/emucore/ROMBanker.cpp new file mode 100644 index 0000000..44df25e --- /dev/null +++ b/arm9/source/emucore/ROMBanker.cpp @@ -0,0 +1,24 @@ + +#include +#include +#include "ROMBanker.h" + +ROMBanker::ROMBanker(ROM* r, UINT16 address, UINT16 tm, UINT16 t, UINT16 mm, UINT16 m) +: RAM(1, address, 0, 0xFFFF), + rom(r), + triggerMask(tm), + trigger(t), + matchMask(mm), + match(m) +{} + +void ROMBanker::reset() +{ + rom->SetEnabled(match == 0); +} + +void ROMBanker::poke(UINT16, UINT16 value) +{ + if ((value & triggerMask) == trigger) + rom->SetEnabled((value & matchMask) == match); +} diff --git a/arm9/source/emucore/ROMBanker.h b/arm9/source/emucore/ROMBanker.h new file mode 100644 index 0000000..3137de1 --- /dev/null +++ b/arm9/source/emucore/ROMBanker.h @@ -0,0 +1,27 @@ + +#ifndef ROMBANKER_H +#define ROMBANKER_H + +#include "RAM.h" +#include "ROM.h" +#include "types.h" + +class ROMBanker : public RAM +{ + +public: + ROMBanker(ROM* rom, UINT16 address, UINT16 triggerMask, UINT16 trigger, UINT16 matchMask, UINT16 match); + + virtual void reset(); + virtual void poke(UINT16 location, UINT16 value); + +private: + ROM* rom; + UINT16 triggerMask; + UINT16 trigger; + UINT16 matchMask; + UINT16 match; + +}; + +#endif diff --git a/arm9/source/emucore/Rip.cpp b/arm9/source/emucore/Rip.cpp new file mode 100644 index 0000000..7e2e0e5 --- /dev/null +++ b/arm9/source/emucore/Rip.cpp @@ -0,0 +1,843 @@ + +#include +#include + +#include "Rip.h" +#include "RAM.h" +#include "ROM.h" +#include "ROMBanker.h" +#include "CRC32.h" + +#define MAX_MEMORY_UNITS 16 +#define MAX_STRING_LENGTH 256 + +#define ROM_TAG_TITLE 0x01 +#define ROM_TAG_PUBLISHER 0x02 +#define ROM_TAG_RELEASE_DATE 0x05 +#define ROM_TAG_COMPATIBILITY 0x06 + +Rip::Rip(UINT32 systemID) +: Peripheral("", ""), + peripheralCount(0), + targetSystemID(systemID), + crc(0) +{ + producer = new CHAR[1]; + strcpy(producer, ""); + year = new CHAR[1]; + strcpy(year, ""); + memset(filename, 0, sizeof(filename)); +} + +Rip::~Rip() +{ + for (UINT16 i = 0; i < peripheralCount; i++) + delete[] peripheralNames[i]; + UINT16 count = GetROMCount(); + for (UINT16 i = 0; i < count; i++) + delete GetROM(i); + delete[] producer; + delete[] year; +} + +void Rip::SetName(const CHAR* n) +{ + if (this->peripheralName) + delete[] peripheralName; + + peripheralName = new CHAR[strlen(n)+1]; + strcpy(peripheralName, n); + printf("RIP Peripheral Name [%s]\n", peripheralName); +} + +void Rip::SetProducer(const CHAR* p) +{ + if (this->producer) + delete[] producer; + + producer = new CHAR[strlen(p)+1]; + strcpy(producer, p); +} + +void Rip::SetYear(const CHAR* y) +{ + if (this->year) + delete[] year; + + year = new CHAR[strlen(y)+1]; + strcpy(year, y); +} + +void Rip::AddPeripheralUsage(const CHAR* periphName, PeripheralCompatibility usage) +{ + peripheralNames[peripheralCount] = new CHAR[strlen(periphName)+1]; + strcpy(peripheralNames[peripheralCount], periphName); + peripheralUsages[peripheralCount] = usage; + peripheralCount++; +} + +PeripheralCompatibility Rip::GetPeripheralUsage(const CHAR* periphName) +{ + for (UINT16 i = 0; i < peripheralCount; i++) { + if (strcmpi(peripheralNames[i], periphName) == 0) + return peripheralUsages[i]; + } + return PERIPH_INCOMPATIBLE; +} + +Rip* Rip::LoadA52(const CHAR* filename) +{ + Rip* rip = new Rip(ID_SYSTEM_ATARI5200); + if (rip == NULL) + return NULL; + + //load the data into the rip + FILE* file = fopen(filename, "rb"); + if (file == NULL) { + delete rip; + return NULL; + } + + //obtain the file size + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + rewind(file); + + //add an appropriate ROM to the rip + ROM* rom = new ROM("Cartridge ROM", "", 0, 1, (UINT16)size, (UINT16)(0xC000-size)); + rip->AddROM(rom); + + //load in the file image + UINT8* image = new UINT8[size]; + UINT32 count = 0; + while (count < size) + image[count++] = (UINT8)fgetc(file); + fclose(file); + + //parse the file image into the rip + UINT32 offset = 0; + UINT16 romCount = rip->GetROMCount(); + for (UINT16 i = 0; i < romCount; i++) + { + if (offset >= size) { + //something is wrong, the file we're trying to load isn't large enough + //getting here indicates a likely incorrect memory map in knowncarts.cfg + delete[] image; + delete rip; + return NULL; + } + ROM* nextRom = rip->GetROM(i); + nextRom->load(image+offset); + offset += nextRom->getByteWidth() * nextRom->getReadSize(); + } + + delete[] image; + + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + + return rip; +} + +Rip* Rip::LoadBin(const CHAR* filename, const CHAR* configFile) +{ + //determine the crc of the designated file + UINT32 crc = CRC32::getCrc(filename); + Rip* rip = LoadCartridgeConfiguration(configFile, crc); + if (rip == NULL) + return NULL; + else printf("Configuration [%s] loaded successfully\n", configFile); + + //load the data into the rip + FILE* file = fopen(filename, "rb"); + if (file == NULL) { + delete rip; + return NULL; + } else printf("File [%s] read into memory successfully\n", filename); + + //obtain the file size + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + rewind(file); + printf("The file size is [%d] bytes\n", (int)size); + + //load in the file image + UINT8* image = new UINT8[size]; + UINT32 count = 0; + while (count < size) + image[count++] = (UINT8)fgetc(file); + fclose(file); + + printf("Parsing file into RIP format\n"); + //parse the file image into the rip + UINT32 offset = 0; + UINT16 romCount = rip->GetROMCount(); + for (UINT16 i = 0; i < romCount; i++) + { + if (offset >= size) + { + //something is wrong, the file we're trying to load isn't large enough + //getting here indicates a likely incorrect memory map in knowncarts.cfg + delete[] image; + delete rip; + return NULL; + } + ROM* nextRom = rip->GetROM(i); + printf("Loading ROM segment [%s %d %d]\n", nextRom->getName(), nextRom->getByteWidth(), nextRom->getReadSize()); + nextRom->load(image+offset); + offset += nextRom->getByteWidth() * nextRom->getReadSize(); + } + + printf("Done parsing...\n"); + delete[] image; + + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + + printf("RIP loaded!\n"); + return rip; +} + +Rip* Rip::LoadCartridgeConfiguration(const CHAR* configFile, UINT32 crc) +{ + CHAR scrc[9]; + sprintf(scrc, "%08X", crc); + + //find the config that matches this crc + FILE* cfgFile = fopen(configFile, "r"); + if (cfgFile == NULL) + return NULL; + + Rip* rip = NULL; + BOOL parseSuccess = FALSE; + CHAR nextLine[256]; + while (!feof(cfgFile)) { + fgets(nextLine, 256, cfgFile); + //size_t length = strlen(nextLine); + if (strstr(nextLine, scrc) != nextLine) { + if (parseSuccess) + break; + else + continue; + } + + CHAR* nextToken; + if ((nextToken = strstr(nextLine, "Info")) != NULL) { + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + + if (rip == NULL) + rip = new Rip(ID_SYSTEM_INTELLIVISION); + rip->SetYear(nextToken+1); + + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + rip->SetProducer(nextToken+1); + + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + rip->SetName(nextToken+1); + + parseSuccess = TRUE; + } + else if ((nextToken = strstr(nextLine, "ROM")) != NULL) { + //TODO: check to make sure we don't exceed the maximum number of roms for a Rip + + //parse rom bit width + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + + //first check to see if this is a banked rom; if there is an equals (=) sign in + //the last field, then we assume we should parse banking + UINT16 triggerAddress, triggerMask, triggerValue, onMask, onValue; + triggerAddress = triggerMask = triggerValue = onMask = onValue = 0; + CHAR* nextEqual = strrchr(nextLine, '='); + if (nextEqual != NULL && nextEqual > nextToken) { + onValue = (UINT16)strtol(nextEqual+1, NULL, 16); + *nextEqual = NULL; + onMask = (UINT16)strtol(nextToken+1, NULL, 16); + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + + nextEqual = strrchr(nextLine, '='); + if (nextEqual == NULL || nextEqual < nextToken) + continue; + + CHAR* nextComma = strrchr(nextLine, ','); + if (nextComma == NULL || nextComma < nextToken || nextComma > nextEqual) + continue; + + triggerValue = (UINT16)strtol(nextEqual+1, NULL, 16); + *nextEqual = NULL; + triggerMask = (UINT16)strtol(nextComma+1, NULL, 16); + *nextComma = NULL; + triggerAddress = (UINT16)strtol(nextToken+1, NULL, 16); + *nextToken = NULL; + + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + } + + UINT8 romBitWidth = (UINT8)atoi(nextToken+1); + + //parse rom size + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + UINT16 romSize = (UINT16)strtol(nextToken+1, NULL, 16); + + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + UINT16 romAddress = (UINT16)strtol(nextToken+1, NULL, 16); + + if (rip == NULL) + rip = new Rip(ID_SYSTEM_INTELLIVISION); + ROM* nextRom = new ROM("Cartridge ROM", "", 0, (romBitWidth+7)/8, romSize, romAddress); + rip->AddROM(nextRom); + if (triggerAddress != 0) + rip->AddRAM(new ROMBanker(nextRom, triggerAddress, triggerMask, triggerValue, onMask, onValue)); + + parseSuccess = TRUE; + } + else if ((nextToken = strstr(nextLine, "RAM")) != NULL) { + //TODO: check to make sure we don't exceed the maximum number of rams for a Rip + + //parse ram width + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + UINT8 ramBitWidth = (UINT8)atoi(nextToken+1); + + //parse ram size + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + UINT16 ramSize = (UINT16)strtol(nextToken+1, NULL, 16); + + //parse ram address + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + UINT16 ramAddress = (UINT16)strtol(nextToken+1, NULL, 16); + + if (rip == NULL) + rip = new Rip(ID_SYSTEM_INTELLIVISION); + rip->AddRAM(new RAM(ramSize, ramAddress, ramBitWidth)); + parseSuccess = TRUE; + } + else if ((nextToken = strstr(nextLine, "Peripheral")) != NULL) { + //TODO: check to make sure we don't exceed the maximum number of peripherals for a Rip + + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + + PeripheralCompatibility pc; + if (strcmpi(nextToken+1, "required") != 0) + pc = PERIPH_REQUIRED; + else if (strcmpi(nextToken+1, "optional") != 0) + pc = PERIPH_OPTIONAL; + else + continue; + + *nextToken = NULL; + if ((nextToken = strrchr(nextLine, ':')) == NULL) + continue; + + if (rip == NULL) + rip = new Rip(ID_SYSTEM_INTELLIVISION); + rip->AddPeripheralUsage(nextToken+1, pc); + parseSuccess = TRUE; + } + } + fclose(cfgFile); + + if (rip != NULL && !parseSuccess) { + delete rip; + rip = NULL; + } + + return rip; +} + +Rip* Rip::LoadRom(const CHAR* filename) +{ + FILE* infile = fopen(filename, "rb"); + if (infile == NULL) { + return NULL; + } + + //read the magic byte (should always be $A8) + int read = fgetc(infile); + if (read != 0xA8) { + fclose(infile); + return NULL; + } + + //read the number of ROM segments + int romSegmentCount = fgetc(infile); + read = fgetc(infile); + if ((read ^ 0xFF) != romSegmentCount) { + fclose(infile); + return NULL; + } + + Rip* rip = new Rip(ID_SYSTEM_INTELLIVISION); + + int i; + for (i = 0; i < romSegmentCount; i++) { + UINT16 start = (UINT16)(fgetc(infile) << 8); + UINT16 end = (UINT16)((fgetc(infile) << 8) | 0xFF); + UINT16 size = (UINT16)((end-start)+1); + + //finally, transfer the ROM image + UINT16* romImage = new UINT16[size]; + int j; + for (j = 0; j < size; j++) { + int nextbyte = fgetc(infile) << 8; + nextbyte |= fgetc(infile); + romImage[j] = (UINT16)nextbyte; + } + rip->AddROM(new ROM("Cartridge ROM", romImage, 2, size, start, 0xFFFF)); + delete[] romImage; + + //read the CRC + fgetc(infile); fgetc(infile); + //TODO: validate the CRC16 instead of just skipping it + //int crc16 = (fgetc(infile) << 8) | fgetc(infile); + //... + } + + //no support currently for the access tables, so skip them + for (i = 0; i < 16; i++) + fgetc(infile); + + //no support currently for the fine address restriction + //tables, so skip them, too + for (i = 0; i < 32; i++) + fgetc(infile); + + while ((read = fgetc(infile)) != -1) { + int length = (read & 0x3F); + read = (read & 0xC) >> 6; + for (i = 0; i < read; i++) + length = (length | (fgetc(infile) << ((8*i)+6))); + + int type = fgetc(infile); + int crc16; + switch (type) { + case ROM_TAG_TITLE: + { + CHAR* title = new char[length*sizeof(char)]; + for (i = 0; i < length; i++) { + read = fgetc(infile); + title[i] = (char)read; + } + crc16 = (fgetc(infile) << 8) | fgetc(infile); + rip->SetName(title); + delete[] title; + } + break; + case ROM_TAG_PUBLISHER: + { + CHAR* publisher = new char[length*sizeof(char)]; + for (i = 0; i < length; i++) { + read = fgetc(infile); + publisher[i] = (char)read; + } + crc16 = (fgetc(infile) << 8) | fgetc(infile); + rip->SetProducer(publisher); + + delete[] publisher; + } + break; + case ROM_TAG_COMPATIBILITY: + { + read = fgetc(infile); + BOOL requiresECS = ((read & 0xC0) == 0x80); + if (requiresECS) + rip->AddPeripheralUsage("ECS", PERIPH_REQUIRED); + for (i = 0; i < length-1; i++) { + fgetc(infile); + fgetc(infile); + } + fgetc(infile); + fgetc(infile); + } + break; + case ROM_TAG_RELEASE_DATE: + default: + { + for (i = 0; i < length; i++) { + fgetc(infile); + fgetc(infile); + } + fgetc(infile); + fgetc(infile); + } + break; + } + } + fclose(infile); + + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + + return rip; +} + +Rip* Rip::LoadRip(const CHAR* filename) +{ + FILE* file = fopen(filename, "rb"); + if (file == NULL) + return NULL; + + //first check the magic number + unsigned int nextUINT32 = freadUINT32(file); + if (nextUINT32 != 0x3F383A34) { + fclose(file); + return NULL; + } + + nextUINT32 = freadUINT32(file); + if (nextUINT32 != 0x7651B5DA) { + fclose(file); + return NULL; + } + + //the magic number is good. this is definitely a RIP file + //start reading the records + Rip* rip = NULL; + unsigned int nextRecordID = freadUINT32(file); + while (!feof(file)) { + unsigned int nextRecordSize = freadUINT32(file); + switch (nextRecordID) { + case ID_HEADER_RECORD: + { + freadUINT16(file); //major file format version + freadUINT16(file); //minor file format version + UINT32 targetSystemID = freadUINT32(file); //target system + rip = new Rip(targetSystemID); + break; + } +/* + case ID_BIOS_COMPAT_RECORD: + tmp1 = ripFile->biosCount; + ripFile->biosCompat[tmp1] = + malloc(sizeof(BiosCompatRecord)); + ripFile->biosCompat[tmp1]->periphNum = fgetc(file); + ripFile->biosCompat[tmp1]->biosTypeNum = fgetc(file); + ripFile->biosCompat[tmp1]->biosNum = fgetc(file); + ripFile->biosCompat[tmp1]->compatibility = fgetc(file); + if (ripFile->biosCompat[tmp1]->compatibility > 2) + ripFile->biosCompat[tmp1]->compatibility = 2; + ripFile->biosCount++; + break; +*/ + case ID_PERIPH_COMPAT_RECORD: + { + if (!rip) + return NULL; + UINT32 periphID = freadUINT32(file); + PeripheralCompatibility usage = (PeripheralCompatibility)(fgetc(file) & 0x03); + rip->AddPeripheralUsage((periphID == ID_PERIPH_ECS ? "ECS" : "Intellivoice"), usage); + break; + } + case ID_NAME_RECORD: + { + if (!rip) + return NULL; + CHAR name[MAX_STRING_LENGTH]; + freadString(file, name, MAX_STRING_LENGTH); + rip->SetName(name); + break; + } + case ID_PRODUCER_RECORD: + { + if (!rip) + return NULL; + CHAR producer[MAX_STRING_LENGTH]; + freadString(file, producer, MAX_STRING_LENGTH); + rip->SetProducer(producer); + break; + } + case ID_YEAR_RECORD: + { + if (!rip) + return NULL; + CHAR year[MAX_STRING_LENGTH]; + freadString(file, year, MAX_STRING_LENGTH); + rip->SetProducer(year); + break; + } + case ID_RAM_RECORD: + { + if (!rip) + return NULL; + + //TODO: handle when we exceed maximum memory units + + UINT8 flags = (UINT8)fgetc(file); + BOOL partialReads = !!(flags & 0x80); + BOOL partialWrites = !!(flags & 0x40); + BOOL banked = !!(flags & 0x20); + UINT8 addressByteWidth = (flags & 0x0F)+1; + + flags = (UINT8)fgetc(file); + UINT8 dataBitWidth = (flags & 0x7F)+1; + UINT16 address = (UINT16)freadInt(file, addressByteWidth); + UINT16 size = (UINT16)freadInt(file, addressByteWidth); + //RAM reset value unused at this point, so skip it + freadInt(file, (dataBitWidth+7)/8); + + UINT16 readMask = 0xFFFF; + if (partialReads) + readMask = (UINT16)freadInt(file, addressByteWidth); + + UINT16 writeMask = 0xFFFF; + if (partialWrites) + writeMask = (UINT16)freadInt(file, addressByteWidth); + + if (banked) { + //banking descriptors not yet supported, so just need to skip 'em + UINT32 bankingDescriptorCount = freadUINT32(file); + for (UINT32 k = 0; k < bankingDescriptorCount; k++) { + freadInt(file, addressByteWidth); //target address width + freadInt(file, addressByteWidth); //write decoding mask + freadInt(file, (dataBitWidth+7)/8); //reset value + UINT32 dataCount = freadUINT32(file); + for (UINT32 l = 0; l < dataCount; l++) + freadInt(file, (dataBitWidth+7)/8); //data banking match values + } + } + + rip->AddRAM(new RAM(size, address, readMask, writeMask, dataBitWidth)); + } + case ID_ROM_RECORD: + { + if (!rip) + return NULL; + + //TODO: handle when we exceed maximum memory units + + UINT8 flags = (UINT8)fgetc(file); + BOOL partialReads = !!(flags & 0x80); + BOOL compressed = !!(flags & 0x40); + BOOL banked = !!(flags & 0x20); + UINT8 addressByteWidth = (flags & 0x0F)+1; + + flags = (UINT8)fgetc(file); + UINT8 dataBitWidth = (flags & 0x7F)+1; + UINT16 address = (UINT16)freadInt(file, addressByteWidth); + + UINT32 arraySize = freadUINT32(file); + UINT8* romImage = NULL; + if (compressed) { + //TODO: support zlib compressed rom images + for (UINT32 k = 0; k < arraySize; k++) + fgetc(file); + arraySize = 0; + romImage = new UINT8[0]; + } + else { + UINT32 dataByteWidth = (dataBitWidth+7)/8; + romImage = new UINT8[arraySize*dataByteWidth]; + for (UINT32 k = 0; k < arraySize; k++) { + for (UINT8 l = 0; l < dataByteWidth; l++) + romImage[(k*dataByteWidth)+(dataByteWidth-l-1)] = (UINT8)fgetc(file); + } + } + + UINT16 readMask = 0xFFFF; + if (partialReads) + readMask = (UINT16)freadInt(file, addressByteWidth); + + if (banked) { + //banking descriptors not yet supported, so just need to skip 'em + UINT32 bankingDescriptorCount = freadUINT32(file); + for (UINT32 k = 0; k < bankingDescriptorCount; k++) { + freadInt(file, addressByteWidth); //target address width + freadInt(file, addressByteWidth); //write decoding mask + freadInt(file, (dataBitWidth+7)/8); //reset value + UINT32 dataCount = freadUINT32(file); + for (UINT32 l = 0; l < dataCount; l++) + freadInt(file, (dataBitWidth+7)/8); //data banking match values + } + } + + rip->AddROM(new ROM("Cartridge ROM", (void*)romImage, (dataBitWidth+7)/8, (UINT16)arraySize, address, readMask)); + delete[] romImage; + + break; + } + default: + { + //unknown record; just skip it + for (UINT32 i = 0; i < nextRecordSize; i++) + fgetc(file); + } + } + nextRecordID = freadUINT32(file); + } + fclose(file); + + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + + return rip; +} + + +BOOL Rip::SaveRip(const CHAR* filename) +{ + FILE* file = fopen(filename, "wb"); + if (file == NULL) { + printf("Error: Unable to create file %s\n", filename); + return 1; + } + + //write the 64-bit magic RIP XBF number + fwriteUINT64(file, RIP_MAGIC_NUMBER); + + //write the header record + fwriteUINT32(file, ID_HEADER_RECORD); + //write the header record size in bytes + fwriteUINT32(file, 9); + //write the major RIP revision number + fwriteUINT16(file, RIP_MAJOR_REVISION); + //write the minor RIP revision number + fwriteUINT16(file, RIP_MINOR_REVISION); + //write the system id + fwriteUINT32(file, targetSystemID); + + //write the name record + fwriteUINT32(file, ID_NAME_RECORD); + fwriteUINT32(file, (UINT32)(4+strlen(GetName()))); + fwriteString(file, GetName()); + + //write the year record + fwriteUINT32(file, ID_YEAR_RECORD); + fwriteUINT32(file, (UINT32)(4+strlen(year))); + fwriteString(file, year); + + //write the producer record + fwriteUINT32(file, ID_PRODUCER_RECORD); + fwriteUINT32(file, (UINT32)(4+strlen(producer))); + fwriteString(file, producer); + + //write the peripheral compatibility records + for (UINT16 i = 0; i < peripheralCount; i++) { + fwriteUINT32(file, ID_PERIPH_COMPAT_RECORD); + fwriteUINT32(file, 5); + //these records are hard-coded for Intellivision now, will have to work out something more + //flexible when Atari 5200 support is re-added in the future + fwriteUINT32(file, (strcmpi(peripheralNames[i], "Intellivoice") == 0) ? ID_PERIPH_INTELLIVOICE : ID_PERIPH_ECS); + fputc(peripheralUsages[i], file); + } + + //write the RAM records + UINT16 ramCount = GetRAMCount(); + for (UINT16 i = 0; i < ramCount; i++) { + RAM* nextMem = GetRAM(i); + fwriteUINT32(file, ID_RAM_RECORD); + UINT16 readMask = nextMem->getReadAddressMask(); + UINT16 writeMask = nextMem->getWriteAddressMask(); + UINT16 byteWidth = (nextMem->getBitWidth()+7)/8; + + //calculate the size of the RAM record + int recordSize = 1 + //flags + 1 + //more flags + 2 + //address + 2 + //size + byteWidth + //reset value + (readMask != 0xFFFF ? 2 : 0) + //read mask + (writeMask != 0xFFFF ? 2 : 0) + //write mask + 0; //banking not supported just yet + fwriteUINT32(file, recordSize); + + //flags #1 + fputc(((readMask != 0xFFFF) ? 0x80 : 0) | + ((writeMask != 0xFFFF) ? 0x40 : 0) | + 1, file); + + //flags #2 + fputc(nextMem->getBitWidth()-1, file); + + //address + fwriteUINT16(file, nextMem->getReadAddress()); + + //size + fwriteUINT16(file, nextMem->getReadSize()); + + //reset value, always zero + for (UINT16 k = 0; k < byteWidth; k++) + fputc(0, file); + + //read mask + if (readMask != 0xFFFF) + fwriteUINT16(file, readMask); + + //write mask + if (writeMask != 0xFFFF) + fwriteUINT16(file, writeMask); + } + + //write the ROM records + UINT16 romCount = GetROMCount(); + for (UINT16 i = 0; i < romCount; i++) { + ROM* nextMem = GetROM(i); + fwriteUINT32(file, ID_ROM_RECORD); + + //calculate the size of the ROM record + UINT16 readMask = nextMem->getReadAddressMask(); + UINT16 byteWidth = nextMem->getByteWidth(); + UINT16 memSize = nextMem->getReadSize(); + int recordSize = 1 + //flags + 1 + //more flags + 2 + //address + 4 + (byteWidth*memSize) + //ROM image + (readMask != 0xFFFF ? 2 : 0) + //read mask + 0; //banking not supported just yet + fwriteUINT32(file, recordSize); + + //flags #1 + fputc(((readMask != 0xFFFF) ? 0x80 : 0) | + 1, file); + + //flags #2 + fputc((byteWidth*8)-1, file); + + //address + UINT16 address = nextMem->getReadAddress(); + fwriteUINT16(file, address); + + //ROM image + fwriteUINT32(file, memSize); + for (UINT16 j = 0; j < memSize; j++) { + switch (byteWidth) { + case 1: + fputc(nextMem->peek(address+j), file); + break; + case 2: + fwriteUINT16(file, nextMem->peek(address+j)); + break; + case 4: + fwriteUINT32(file, nextMem->peek(address+j)); + break; + default: + for (UINT8 k = 0; k < byteWidth; k++) + fputc(0, file); + } + } + + //read mask + if (readMask != 0xFFFF) + fwriteUINT16(file, readMask); + } + + fclose(file); + + return 0; +} diff --git a/arm9/source/emucore/Rip.h b/arm9/source/emucore/Rip.h new file mode 100644 index 0000000..8ae19b3 --- /dev/null +++ b/arm9/source/emucore/Rip.h @@ -0,0 +1,87 @@ + +#ifndef RIP_H +#define RIP_H + +#include +#include +#include "ripxbf.h" +#include "types.h" +#include "Peripheral.h" +#include "Memory.h" + +using namespace std; + +#define MAX_BIOSES 16 +#define MAX_PERIPHERALS 16 + +typedef struct _CartridgeConfiguration CartridgeConfiguration; + +class Rip : public Peripheral +{ + +public: + virtual ~Rip(); + + void SetTargetSystemID(UINT32 t) { this->targetSystemID = t; } + UINT32 GetTargetSystemID() { return targetSystemID; } + + void SetName(const CHAR* p); + + void SetProducer(const CHAR* p); + const CHAR* GetProducer() { return producer; } + + void SetYear(const CHAR* y); + const CHAR* GetYear() { return year; } + + PeripheralCompatibility GetPeripheralUsage(const CHAR* periphName); + + //load a regular .rip file + static Rip* LoadRip(const CHAR* filename); + + //load a raw binary Atari 5200 image of a game + static Rip* LoadA52(const CHAR* filename); + + //load a raw binary Intellivision image of a game + static Rip* LoadBin(const CHAR* filename, const CHAR* cfgFilename); + + //load a raw binary image contained within a .zip file + static Rip* LoadZip(const CHAR* filename, const CHAR* cfgFilename); + + //load an Intellivision .rom file + static Rip* LoadRom(const CHAR* filename); + + BOOL SaveRip(const CHAR* filename); + + const CHAR* GetFileName() { + return this->filename; + } + + UINT32 GetCRC() { + return this->crc; + } + +private: + Rip(UINT32 systemID); + //Rip(UINT32 systemID, const CHAR* nme, const CHAR* prducer, const CHAR* yr); + + void AddPeripheralUsage(const CHAR* periphName, PeripheralCompatibility usage); + static Rip* LoadCartridgeConfiguration(const CHAR* cfgFile, UINT32 crc); + + void SetFileName(const CHAR* fname) { + strncpy(this->filename, fname, sizeof(this->filename)); + } + + UINT32 targetSystemID; + CHAR* producer; + CHAR* year; + + //peripheral compatibility indicators + CHAR* peripheralNames[MAX_PERIPHERALS]; + PeripheralCompatibility peripheralUsages[MAX_PERIPHERALS]; + UINT32 peripheralCount; + + CHAR filename[MAX_PATH]; + UINT32 crc; +}; + +#endif diff --git a/arm9/source/emucore/SP0256.cpp b/arm9/source/emucore/SP0256.cpp new file mode 100644 index 0000000..40fd1cb --- /dev/null +++ b/arm9/source/emucore/SP0256.cpp @@ -0,0 +1,956 @@ +#include +#include "SP0256.h" + +INT32 repeat __attribute__((section(".dtcm"))); +INT32 period __attribute__((section(".dtcm"))); +INT32 periodCounter __attribute__((section(".dtcm"))); +INT32 amplitude __attribute__((section(".dtcm"))); +INT8 b[6] __attribute__((section(".dtcm"))); +INT8 f[6] __attribute__((section(".dtcm"))); +INT32 y[6][2] __attribute__((section(".dtcm"))); +INT8 periodInterpolation __attribute__((section(".dtcm"))); +INT8 amplitudeInterpolation __attribute__((section(".dtcm"))); + + +UINT16 bitMasks[16] __attribute__((section(".dtcm"))) = { + 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +INT16 qtbl[256] __attribute__((section(".dtcm"))) = { + 0, 511, 510, 509, 508, 507, 506, 505, + 504, 503, 502, 501, 500, 499, 498, 497, + 496, 495, 494, 493, 492, 491, 490, 489, + 488, 487, 486, 485, 484, 483, 482, 481, + 479, 477, 475, 473, 471, 469, 467, 465, + 463, 461, 459, 457, 455, 453, 451, 449, + 447, 445, 443, 441, 439, 437, 435, 433, + 431, 429, 427, 425, 421, 417, 413, 409, + 405, 401, 397, 393, 389, 385, 381, 377, + 373, 369, 365, 361, 357, 353, 349, 345, + 341, 337, 333, 329, 325, 321, 317, 313, + 309, 305, 301, 297, 289, 281, 273, 265, + 257, 249, 241, 233, 225, 217, 209, 201, + 193, 185, 177, 169, 161, 153, 145, 137, + 129, 121, 113, 105, 97, 89, 81, 73, + 65, 57, 49, 41, 33, 25, 12, 9, + 0, -9, -12, -25, -33, -41, -49, -57, + -65, -73, -81, -89, -97, -105, -113, -121, + -129, -137, -145, -153, -161, -169, -177, -185, + -193, -201, -209, -217, -225, -233, -241, -249, + -257, -265, -273, -281, -289, -297, -301, -305, + -309, -313, -317, -321, -325, -329, -333, -337, + -341, -345, -349, -353, -357, -361, -365, -369, + -373, -377, -381, -385, -389, -393, -397, -401, + -405, -409, -413, -417, -421, -425, -427, -429, + -431, -433, -435, -437, -439, -441, -443, -445, + -447, -449, -451, -453, -455, -457, -459, -461, + -463, -465, -467, -469, -471, -473, -475, -477, + -479, -481, -482, -483, -484, -485, -486, -487, + -488, -489, -490, -491, -492, -493, -494, -495, + -496, -497, -498, -499, -500, -501, -502, -503, + -504, -505, -506, -507, -508, -509, -510, -511 +}; + +SP0256::SP0256() + : Processor("SP0256"), + ivoiceROM("Intellivoice ROM", "ivoice.bin", 0, 1, 0x800, 0x1000, TRUE) +{ + registers.init(this); +} + +INT32 SP0256::getClockSpeed() { + return 10000; +} + +INT32 SP0256::getClocksPerSample() { + return 1; +} + +void SP0256::resetProcessor() +{ + currentBits = 0; + bitsLeft = 0; + + pc = 0; + page = 1; + stack = 0; + mode = 0; + repeatPrefix = 0; + command = 0; + lrqHigh = TRUE; + idle = TRUE; + fifoHead = 0; + fifoSize = 0; + speaking = FALSE; + + amplitude = 0; + period = 0; + periodCounter = 0x1; + random = 1; + for (INT32 i = 0; i < 6; i++) { + y[i][0] = 0; + y[i][1] = 0; + } +} + +INT32 SP0256::tick(INT32 minimum) +{ + if (idle) { + //for (int i = 0; i < minimum; i++) + // audioOutputLine->playSample(0); + return minimum; + } + + INT32 totalTicks = 0; + do { + + if (!speaking) { + speaking = TRUE; + lrqHigh = TRUE; + pc = 0x1000 | (command << 1); + bitsLeft = 0; + command = 0; + } + + //if the speaking filters are empty, fill 'em up + while (!idle && repeat == 0) { + INT32 repeatBefore = repeatPrefix; + decode(); + if (repeatBefore != 0) + repeatPrefix = 0; + } + + INT32 sample = 0; + if (period == 0) { + if (periodCounter == 0) { + periodCounter = 64; + repeat--; + for (UINT8 j = 0; j < 6; j++) + y[j][0] = y[j][1] = 0; + } + else + periodCounter--; + + sample = ((amplitude & 0x1F) << ((amplitude & 0xE0) >> 5)); + BOOL noise = ((random & 1) != 0); + random = (random >> 1) ^ (noise ? 0x14000 : 0); + if (!noise) + sample = -sample; + } + else { + if (periodCounter == 0) { + periodCounter = period; + repeat--; + sample = ((amplitude & 0x1F) << ((amplitude & 0xE0) >> 5)); + for (INT32 j = 0; j < 6; j++) + y[j][0] = y[j][1] = 0; + + } + else + periodCounter--; + } + + period = ((period | 0x10000) + periodInterpolation) & 0xFFFF; + amplitude = ((amplitude | 0x10000) + amplitudeInterpolation) & 0xFFFF; + + for (INT32 i = 0; i < 6; i++) { + sample += ((qtbl[0x80+b[i]]*y[i][1]) >> 9); + sample += ((qtbl[0x80+f[i]]*y[i][0]) >> 8); + y[i][1] = y[i][0]; + y[i][0] = sample; + } + + //clamp the sample to a 12-bit range + if (sample > 2047) sample = 2047; + if (sample < -2048) sample = -2048; + + audioOutputLine->playSample((INT16)(sample << 4)); + + totalTicks++; + + } while (totalTicks < minimum); + return totalTicks; +} + +INT8 SP0256::readDelta(INT32 numBits) { + INT32 value = readBits(numBits); + if ((value & (1 << (numBits - 1))) != 0) + value |= -1 << numBits; + return (INT8)value; +} + +INT32 SP0256::readBitsReverse(INT32 numBits) +{ + while (bitsLeft < numBits) { + if (pc < 0x1800) { + currentBits |= (ivoiceROM.peek((UINT16)pc) << bitsLeft); + bitsLeft += 8; + pc = (pc+1) & 0xFFFF; + } + else if (pc == 0x1800 && fifoSize > 0) { + currentBits |= (fifoBytes[fifoHead] << bitsLeft); + fifoHead = (fifoHead+1) & 0x3F; + fifoSize--; + bitsLeft += 10; + } + else { + //error, read outside of bounds + currentBits |= (0x03FF << bitsLeft); + bitsLeft += 10; + pc = (pc+1) & 0xFFFF; + } + + } + + INT32 output = currentBits & bitMasks[numBits-1]; + output = flipEndian(output, numBits); + currentBits = currentBits >> numBits; + bitsLeft -= numBits; + return output; +} + +INT32 SP0256::readBits(INT32 numBits) +{ + while (bitsLeft < numBits) { + if (pc < 0x1800) { + currentBits |= (ivoiceROM.peek((UINT16)pc) << bitsLeft); + bitsLeft += 8; + pc = (pc+1) & 0xFFFF; + } + else if (pc == 0x1800 && fifoSize > 0) { + currentBits |= (fifoBytes[fifoHead] << bitsLeft); + fifoHead = (fifoHead+1) & 0x3F; + fifoSize--; + bitsLeft += 10; + } + else { + //error, read outside of bounds + currentBits |= (0x03FF << bitsLeft); + bitsLeft += 10; + pc = (pc+1) & 0xFFFF; + } + + } + + INT32 output = currentBits & bitMasks[numBits-1]; + currentBits = currentBits >> numBits; + bitsLeft -= numBits; + return output; +} + +void SP0256::RTS() { + if (stack == 0) { + if (!lrqHigh) { + pc = 0x1000 | (command << 1); + bitsLeft = 0; + command = 0; + lrqHigh = TRUE; + } + else { + speaking = FALSE; + idle = TRUE; + } + } + else { + pc = stack; + stack = 0; + bitsLeft = 0; + } +} + +void SP0256::SETPAGE(INT32 immed4) { + this->page = flipEndian(immed4, 4); +} + +void SP0256::LOADALL(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = readBits(8); + period = readBits(8); + //periodCounter = (period == 0 ? 0x100 : period); + b[0] = (INT8)readBits(8); + f[0] = (INT8)readBits(8); + b[1] = (INT8)readBits(8); + f[1] = (INT8)readBits(8); + b[2] = (INT8)readBits(8); + f[2] = (INT8)readBits(8); + b[3] = (INT8)readBits(8); + f[3] = (INT8)readBits(8); + b[4] = (INT8)readBits(8); + f[4] = (INT8)readBits(8); + b[5] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + if ((mode & 0x01) == 0) { + amplitudeInterpolation = 0; + periodInterpolation = 0; + } + else { + amplitudeInterpolation = (INT8)readBits(8); + periodInterpolation = (INT8)readBits(8); + } +} + +void SP0256::LOAD_2(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + period = readBits(8); + //periodCounter = (period == 0 ? 0x100 : period); + switch (mode) { + case 0x0: + b[0] = (INT8)((readBits(3) << 4) | (b[0] & 0x0F)); + f[0] = (INT8)((readBits(5) << 3) | (f[0] & 0x07)); + b[1] = (INT8)((readBits(3) << 4) | (b[1] & 0x0F)); + f[1] = (INT8)((readBits(5) << 3) | (f[1] & 0x07)); + b[2] = (INT8)((readBits(3) << 4) | (b[2] & 0x0F)); + f[2] = (INT8)((readBits(5) << 3) | (f[2] & 0x07)); + b[3] = (INT8)((readBits(4) << 3) | (b[3] & 0x07)); + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + b[4] = (INT8)((readBits(7) << 1) | (b[4] & 0x01)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + b[5] = 0; + f[5] = 0; + break; + case 0x1: + b[0] = (INT8)((readBits(3) << 4) | (b[0] & 0x0F)); + f[0] = (INT8)((readBits(5) << 3) | (f[0] & 0x07)); + b[1] = (INT8)((readBits(3) << 4) | (b[1] & 0x0F)); + f[1] = (INT8)((readBits(5) << 3) | (f[1] & 0x07)); + b[2] = (INT8)((readBits(3) << 4) | (b[2] & 0x0F)); + f[2] = (INT8)((readBits(5) << 3) | (f[2] & 0x07)); + b[3] = (INT8)((readBits(4) << 3) | (b[3] & 0x07)); + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + b[4] = (INT8)((readBits(7) << 1) | (b[4] & 0x01)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + b[5] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + break; + case 0x2: + b[0] = (INT8)((readBits(6) << 1) | (b[0] & 0x01)); + f[0] = (INT8)((readBits(6) << 2) | (f[0] & 0x03)); + b[1] = (INT8)((readBits(6) << 1) | (b[1] & 0x01)); + f[1] = (INT8)((readBits(6) << 2) | (f[1] & 0x03)); + b[2] = (INT8)((readBits(6) << 1) | (b[2] & 0x01)); + f[2] = (INT8)((readBits(6) << 2) | (f[2] & 0x03)); + b[3] = (INT8)((readBits(6) << 1) | (b[3] & 0x01)); + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + b[4] = (INT8)readBits(8); + f[4] = (INT8)readBits(8); + b[5] = 0; + f[5] = 0; + break; + case 0x3: + b[0] = (INT8)((readBits(6) << 1) | (b[0] & 0x01)); + f[0] = (INT8)((readBits(6) << 2) | (f[0] & 0x03)); + b[1] = (INT8)((readBits(6) << 1) | (b[1] & 0x01)); + f[1] = (INT8)((readBits(6) << 2) | (f[1] & 0x03)); + b[2] = (INT8)((readBits(6) << 1) | (b[2] & 0x01)); + f[2] = (INT8)((readBits(6) << 2) | (f[2] & 0x03)); + b[3] = (INT8)((readBits(6) << 1) | (b[3] & 0x01)); + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + b[4] = (INT8)readBits(8); + f[4] = (INT8)readBits(8); + b[5] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + break; + } + + amplitudeInterpolation = (INT8) + ((amplitudeInterpolation & 0xE0) | (readBits(5))); + periodInterpolation = (INT8) + ((periodInterpolation & 0xE0) | (readBits(5))); +} + +void SP0256::SETMSB_3(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + if (mode == 0x00 || mode == 0x02) { + b[5] = 0; + f[5] = 0; + } + + switch (mode) { + case 0x0: + case 0x1: + f[0] = (INT8)((readBits(5) << 3) | (f[0] & 0x07)); + f[1] = (INT8)((readBits(5) << 3) | (f[1] & 0x07)); + f[2] = (INT8)((readBits(5) << 3) | (f[2] & 0x07)); + break; + case 0x2: + case 0x3: + f[0] = (INT8)((readBits(6) << 2) | (f[0] & 0x03)); + f[1] = (INT8)((readBits(6) << 2) | (f[1] & 0x03)); + f[2] = (INT8)((readBits(6) << 2) | (f[2] & 0x03)); + break; + } + + amplitudeInterpolation = (INT8) + ((amplitudeInterpolation & 0xE0) | (readBits(5))); + periodInterpolation = (INT8) + ((periodInterpolation & 0xE0) | (readBits(5))); +} + +void SP0256::LOAD_4(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + period = readBits(8); + //periodCounter = (period == 0 ? 0x100 : period); + b[0] = 0; + f[0] = 0; + b[1] = 0; + f[1] = 0; + b[2] = 0; + f[2] = 0; + switch (mode) { + case 0x0: + b[3] = (INT8)((readBits(4) << 3) | (b[3] & 0x07)); + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + b[4] = (INT8)((readBits(7) << 1) | (b[4] & 0x01)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + b[5] = 0; + f[5] = 0; + break; + case 0x1: + b[3] = (INT8)((readBits(4) << 3) | (b[3] & 0x07)); + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + b[4] = (INT8)((readBits(7) << 1) | (b[4] & 0x01)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + b[5] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + break; + case 0x2: + b[3] = (INT8)((readBits(6) << 1) | (b[3] & 0x01)); + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + b[4] = (INT8)readBits(8); + f[4] = (INT8)readBits(8); + b[5] = 0; + f[5] = 0; + break; + case 0x3: + b[3] = (INT8)((readBits(6) << 1) | (b[3] & 0x01)); + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + b[4] = (INT8)readBits(8); + f[4] = (INT8)readBits(8); + b[5] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + break; + } + + amplitudeInterpolation = 0; + periodInterpolation = 0; +} + +void SP0256::SETMSB_5(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + period = readBits(8); + //periodCounter = (period == 0 ? 0x100 : period); + if (mode == 0x00 || mode == 0x02) { + b[5] = 0; + f[5] = 0; + } + + switch (mode) { + case 0x0: + case 0x1: + f[0] = (INT8)((readBits(5) << 3) | (f[0] & 0x07)); + f[1] = (INT8)((readBits(5) << 3) | (f[1] & 0x07)); + f[2] = (INT8)((readBits(5) << 3) | (f[2] & 0x07)); + break; + case 0x2: + case 0x3: + f[0] = (INT8)((readBits(6) << 2) | (f[0] & 0x03)); + f[1] = (INT8)((readBits(6) << 2) | (f[1] & 0x03)); + f[2] = (INT8)((readBits(6) << 2) | (f[2] & 0x03)); + break; + } +} + +void SP0256::SETMSB_6(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + if (mode == 0x00 || mode == 0x02) { + b[5] = 0; + f[5] = 0; + } + + switch (mode) { + case 0x0: + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + b[5] = 0; + f[5] = 0; + break; + case 0x1: + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + f[5] = (INT8)readBits(8); + break; + case 0x2: + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + f[4] = (INT8)readBits(8); + b[5] = 0; + f[5] = 0; + break; + case 0x3: + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + f[4] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + break; + } +} + +void SP0256::JMP(INT32 immed4) { + pc = (page << 12) | (flipEndian(immed4, 4) << 8) | readBitsReverse(8); + bitsLeft = 0; +} + +void SP0256::SETMODE(INT32 immed4) { + immed4 = flipEndian(immed4, 4); + mode = immed4 & 0x3; + repeatPrefix = (immed4 & 0xC) >> 2; +} + +void SP0256::DELTA_9(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (amplitude + 0x10000 + (readDelta(4) << 2)) + & 0xFFFF; + period = (period + 0x10000 + readDelta(5)) & 0xFFFF; + //periodCounter = (period == 0 ? 0x100 : period); + switch (mode) { + case 0x0: + b[0] += readDelta(3) << 4; + f[0] += readDelta(3) << 3; + b[1] += readDelta(3) << 4; + f[1] += readDelta(3) << 3; + b[2] += readDelta(3) << 4; + f[2] += readDelta(3) << 3; + b[3] += readDelta(3) << 3; + f[3] += readDelta(4) << 2; + b[4] += readDelta(4) << 1; + f[4] += readDelta(4) << 2; + break; + case 0x1: + b[0] += readDelta(3) << 4; + f[0] += readDelta(3) << 3; + b[1] += readDelta(3) << 4; + f[1] += readDelta(3) << 3; + b[2] += readDelta(3) << 4; + f[2] += readDelta(3) << 3; + b[3] += readDelta(3) << 3; + f[3] += readDelta(4) << 2; + b[4] += readDelta(4) << 1; + f[4] += readDelta(4) << 2; + b[5] += readDelta(5) << 0; + f[5] += readDelta(5) << 0; + break; + case 0x2: + b[0] += readDelta(4) << 2; + f[0] += readDelta(4) << 0; + b[1] += readDelta(4) << 1; + f[1] += readDelta(4) << 2; + b[2] += readDelta(4) << 1; + f[2] += readDelta(4) << 2; + b[3] += readDelta(4) << 1; + f[3] += readDelta(5) << 2; + b[4] += readDelta(5) << 1; + f[4] += readDelta(5) << 1; + break; + case 0x3: + b[0] += readDelta(4) << 2; + f[0] += readDelta(4) << 0; + b[1] += readDelta(4) << 1; + f[1] += readDelta(4) << 2; + b[2] += readDelta(4) << 1; + f[2] += readDelta(4) << 2; + b[3] += readDelta(4) << 1; + f[3] += readDelta(5) << 2; + b[4] += readDelta(5) << 1; + f[4] += readDelta(5) << 1; + b[5] += readDelta(5) << 0; + f[5] += readDelta(5) << 0; + break; + } +} + +void SP0256::SETMSB_A(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + switch (mode) { + case 0x0: + case 0x1: + f[0] = (INT8)((readBits(5) << 3) | (f[0] & 0x07)); + f[1] = (INT8)((readBits(5) << 3) | (f[1] & 0x07)); + f[2] = (INT8)((readBits(5) << 3) | (f[2] & 0x07)); + break; + case 0x2: + case 0x3: + f[0] = (INT8)((readBits(6) << 2) | (f[0] & 0x03)); + f[1] = (INT8)((readBits(6) << 2) | (f[1] & 0x03)); + f[2] = (INT8)((readBits(6) << 2) | (f[2] & 0x03)); + break; + } +} + +void SP0256::JSR(INT32 immed4) { + INT32 newpc = (page << 12) | (flipEndian(immed4, 4) << 8) | readBitsReverse(8); + stack = pc; + pc = newpc; + bitsLeft = 0; +} + +void SP0256::LOAD_C(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + period = readBits(8); + //periodCounter = (period == 0 ? 0x100 : period); + switch (mode) { + case 0x0: + b[0] = (INT8)((readBits(3) << 4) | (b[0] & 0x0F)); + f[0] = (INT8)((readBits(5) << 3) | (f[0] & 0x07)); + b[1] = (INT8)((readBits(3) << 4) | (b[1] & 0x0F)); + f[1] = (INT8)((readBits(5) << 3) | (f[1] & 0x07)); + b[2] = (INT8)((readBits(3) << 4) | (b[2] & 0x0F)); + f[2] = (INT8)((readBits(5) << 3) | (f[2] & 0x07)); + b[3] = (INT8)((readBits(4) << 3) | (b[3] & 0x07)); + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + b[4] = (INT8)((readBits(7) << 1) | (b[4] & 0x01)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + b[5] = 0; + f[5] = 0; + break; + case 0x1: + b[0] = (INT8)((readBits(3) << 4) | (b[0] & 0x0F)); + f[0] = (INT8)((readBits(5) << 3) | (f[0] & 0x07)); + b[1] = (INT8)((readBits(3) << 4) | (b[1] & 0x0F)); + f[1] = (INT8)((readBits(5) << 3) | (f[1] & 0x07)); + b[2] = (INT8)((readBits(3) << 4) | (b[2] & 0x0F)); + f[2] = (INT8)((readBits(5) << 3) | (f[2] & 0x07)); + b[3] = (INT8)((readBits(4) << 3) | (b[3] & 0x07)); + f[3] = (INT8)((readBits(6) << 2) | (f[3] & 0x03)); + b[4] = (INT8)((readBits(7) << 1) | (b[4] & 0x01)); + f[4] = (INT8)((readBits(6) << 2) | (f[4] & 0x03)); + b[5] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + break; + case 0x2: + b[0] = (INT8)((readBits(6) << 1) | (b[0] & 0x01)); + f[0] = (INT8)((readBits(6) << 2) | (f[0] & 0x03)); + b[1] = (INT8)((readBits(6) << 1) | (b[1] & 0x01)); + f[1] = (INT8)((readBits(6) << 2) | (f[1] & 0x03)); + b[2] = (INT8)((readBits(6) << 1) | (b[2] & 0x01)); + f[2] = (INT8)((readBits(6) << 2) | (f[2] & 0x03)); + b[3] = (INT8)((readBits(6) << 1) | (b[3] & 0x01)); + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + b[4] = (INT8)readBits(8); + f[4] = (INT8)readBits(8); + b[5] = 0; + f[5] = 0; + break; + case 0x3: + b[0] = (INT8)((readBits(6) << 1) | (b[0] & 0x01)); + f[0] = (INT8)((readBits(6) << 2) | (f[0] & 0x03)); + b[1] = (INT8)((readBits(6) << 1) | (b[1] & 0x01)); + f[1] = (INT8)((readBits(6) << 2) | (f[1] & 0x03)); + b[2] = (INT8)((readBits(6) << 1) | (b[2] & 0x01)); + f[2] = (INT8)((readBits(6) << 2) | (f[2] & 0x03)); + b[3] = (INT8)((readBits(6) << 1) | (b[3] & 0x01)); + f[3] = (INT8)((readBits(7) << 1) | (f[3] & 0x01)); + b[4] = (INT8)readBits(8); + f[4] = (INT8)readBits(8); + b[5] = (INT8)readBits(8); + f[5] = (INT8)readBits(8); + break; + } + + amplitudeInterpolation = 0; + periodInterpolation = 0; +} + +void SP0256::DELTA_D(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (amplitude + 0x10000 + (readDelta(4) << 2)) + & 0xFFFF; + period = (period + 0x10000 + readDelta(5)) & 0xFFFF; + //periodCounter = (period == 0 ? 0x100 : period); + switch (mode) { + case 0x0: + b[3] += readDelta(3) << 3; + f[3] += readDelta(4) << 2; + b[4] += readDelta(4) << 1; + f[4] += readDelta(4) << 2; + break; + case 0x1: + b[3] += readDelta(3) << 3; + f[3] += readDelta(4) << 2; + b[4] += readDelta(4) << 1; + f[4] += readDelta(4) << 2; + b[5] += readDelta(5) << 0; + f[5] += readDelta(5) << 0; + break; + case 0x2: + b[3] += readDelta(4) << 1; + f[3] += readDelta(5) << 1; + b[4] += readDelta(5) << 0; + f[4] += readDelta(5) << 0; + break; + case 0x3: + b[3] += readDelta(4) << 1; + f[3] += readDelta(5) << 1; + b[4] += readDelta(5) << 0; + f[4] += readDelta(5) << 0; + b[5] += readDelta(5) << 0; + f[5] += readDelta(5) << 0; + break; + } +} + +void SP0256::LOAD_E(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + amplitude = (readBits(6) << 2) | (amplitude & 0x03); + period = readBits(8); + //periodCounter = (period == 0 ? 0x100 : period); +} + +void SP0256::PAUSE(INT32 immed4) { + repeat = (repeatPrefix << 4) | immed4; + if (repeat == 0) + return; + + //clear everything + amplitude = 0; + period = 0; + for (INT32 j = 0; j < 6; j++) { + b[j] = 0; + f[j] = 0; + } + amplitudeInterpolation = 0; + periodInterpolation = 0; +} + +void SP0256::decode() { + INT32 immed4 = readBits(4); + INT32 nextInstruction = readBitsReverse(4); + switch (nextInstruction) { + case 0x0: + if (immed4 == 0) + RTS(); + else + SETPAGE(immed4); + break; + case 0x8: + SETMODE(immed4); + break; + case 0x4: + LOAD_4(immed4); + break; + case 0xC: + LOAD_C(immed4); + break; + case 0x2: + LOAD_2(immed4); + break; + case 0xA: + SETMSB_A(immed4); + break; + case 0x6: + SETMSB_6(immed4); + break; + case 0xE: + LOAD_E(immed4); + break; + case 0x1: + LOADALL(immed4); + break; + case 0x9: + DELTA_9(immed4); + break; + case 0x5: + SETMSB_5(immed4); + break; + case 0xD: + DELTA_D(immed4); + break; + case 0x3: + SETMSB_3(immed4); + break; + case 0xB: + JSR(immed4); + break; + case 0x7: + JMP(immed4); + break; + case 0xF: + PAUSE(immed4); + break; + +/* + case 0x0: + if (immed4 == 0) + RTS(); + else + SETPAGE(immed4); + break; + case 0x1: + SETMODE(immed4); + break; + case 0x2: + LOAD_4(immed4); + break; + case 0x3: + LOAD_C(immed4); + break; + case 0x4: + LOAD_2(immed4); + break; + case 0x5: + SETMSB_A(immed4); + break; + case 0x6: + SETMSB_6(immed4); + break; + case 0x7: + LOAD_E(immed4); + break; + case 0x8: + LOADALL(immed4); + break; + case 0x9: + DELTA_9(immed4); + break; + case 0xA: + SETMSB_5(immed4); + break; + case 0xB: + DELTA_D(immed4); + break; + case 0xC: + SETMSB_3(immed4); + break; + case 0xD: + JSR(immed4); + break; + case 0xE: + JMP(immed4); + break; + case 0xF: + PAUSE(immed4); + break; +*/ + } +} + +INT32 SP0256::flipEndian(INT32 value, INT32 bits) { + INT32 output = 0; + INT32 bitMask = 1; + for (INT32 i = 0; i < bits; i++) { + INT32 offset = (bits-1)-(i<<1); + if (offset > 0) + output |= (value & bitMask) << offset; + else + output |= (value & bitMask) >> -offset; + bitMask = bitMask << 1; + } + return output; +} + +SP0256State SP0256::getState() +{ + SP0256State state = {0}; +#if 0 + state.bitsLeft = this->bitsLeft; + state.currentBits = this->currentBits; + + state.pc = this->pc; + state.stack = this->stack; + state.mode = this->mode; + state.repeatPrefix = this->repeatPrefix; + state.page = this->page; + state.command = this->command; + + state.idle = this->idle; + state.lrqHigh = this->lrqHigh; + state.speaking = this->speaking; + memcpy(state.fifoBytes, this->fifoBytes, sizeof(this->fifoBytes)); + state.fifoHead = this->fifoHead; + state.fifoSize = this->fifoSize; + + state.repeat = this->repeat; + state.period = this->period; + state.periodCounter = this->periodCounter; + state.amplitude = this->amplitude; + memcpy(state.b, this->b, sizeof(this->b)); + memcpy(state.f, this->f, sizeof(this->f)); + memcpy(state.y, this->y, sizeof(this->f)); + state.periodInterpolation = this->periodInterpolation; + state.amplitudeInterpolation = this->amplitudeInterpolation; + + state.random = this->random; +#endif + return state; +} + +void SP0256::setState(SP0256State state) +{ +#if 0 + this->bitsLeft = state.bitsLeft; + this->currentBits = state.currentBits; + + this->pc = state.pc; + this->stack = state.stack; + this->mode = state.mode; + this->repeatPrefix = state.repeatPrefix; + this->page = state.page; + this->command = state.command; + + this->idle = state.idle; + this->lrqHigh = state.lrqHigh; + this->speaking = state.speaking; + memcpy(this->fifoBytes, state.fifoBytes, sizeof(this->fifoBytes)); + this->fifoHead = state.fifoHead; + this->fifoSize = state.fifoSize; + + this->repeat = state.repeat; + this->period = state.period; + this->periodCounter = state.periodCounter; + this->amplitude = state.amplitude; + memcpy(this->b, state.b, sizeof(this->b)); + memcpy(this->f, state.f, sizeof(this->f)); + memcpy(this->y, state.y, sizeof(this->f)); + this->periodInterpolation = state.periodInterpolation; + this->amplitudeInterpolation = state.amplitudeInterpolation; + + this->random = state.random; +#endif +} diff --git a/arm9/source/emucore/SP0256.h b/arm9/source/emucore/SP0256.h new file mode 100644 index 0000000..346b30c --- /dev/null +++ b/arm9/source/emucore/SP0256.h @@ -0,0 +1,125 @@ + +#ifndef MICROSEQUENCER_H +#define MICROSEQUENCER_H + +#include "AudioProducer.h" +#include "SP0256_Registers.h" +#include "types.h" +#include "Processor.h" +#include "ROM.h" +#include "AudioOutputLine.h" + +#define FIFO_LOCATION 0x1800 +#define FIFO_MAX_SIZE 64 +//#define FIFO_END (FIFO_LOCATION + FIFO_MAX_SIZE) + + + //registers +extern INT32 repeat; +extern INT32 period; +extern INT32 periodCounter; +extern INT32 amplitude; +extern INT8 b[6]; +extern INT8 f[6]; +extern INT32 y[6][2]; +extern INT8 periodInterpolation; +extern INT8 amplitudeInterpolation; + + + +TYPEDEF_STRUCT_PACK( _SP0256State +{ + INT32 bitsLeft; + INT32 currentBits; + INT32 pc; + INT32 stack; + INT32 mode; + INT32 repeatPrefix; + INT32 page; + INT32 command; + INT32 repeat; + INT32 period; + INT32 periodCounter; + INT32 amplitude; + INT32 random; + INT32 fifoHead; + INT32 fifoSize; + INT32 fifoBytes[64]; + INT32 y[6][2]; + INT8 b[6]; + INT8 f[6]; + INT8 periodInterpolation; + INT8 amplitudeInterpolation; + INT8 idle; + INT8 lrqHigh; + INT8 speaking; + UINT8 _pad6[3]; +} SP0256State; ) + +class SP0256 : public Processor, public AudioProducer +{ + + friend class SP0256_Registers; + + public: + SP0256(); + void resetProcessor(); + INT32 getClockSpeed(); + INT32 getClocksPerSample(); + INT32 getSampleRate() { return getClockSpeed(); } + INT32 tick(INT32); + inline BOOL isIdle() { return idle; } + + SP0256State getState(); + void setState(SP0256State state); + + SP0256_Registers registers; + ROM ivoiceROM; + + private: + INT8 readDelta(INT32 numBits); + INT32 readBits(INT32 numBits); + INT32 readBitsReverse(INT32 numBits); + void RTS(); + void SETPAGE(INT32 immed4); + void LOADALL(INT32 immed4); + void LOAD_2(INT32 immed4); + void SETMSB_3(INT32 immed4); + void LOAD_4(INT32 immed4); + void SETMSB_5(INT32 immed4); + void SETMSB_6(INT32 immed4); + void JMP(INT32 immed4); + void SETMODE(INT32 immed4); + void DELTA_9(INT32 immed4); + void SETMSB_A(INT32 immed4); + void JSR(INT32 immed4); + void LOAD_C(INT32 immed4); + void DELTA_D(INT32 immed4); + void LOAD_E(INT32 immed4); + void PAUSE(INT32 immed4); + void decode(); + static INT32 flipEndian(INT32 value, INT32 bits); + + INT32 bitsLeft; + INT32 currentBits; + + //registers + INT32 pc; + INT32 stack; + INT32 mode; + INT32 repeatPrefix; + INT32 page; + INT32 command; + + BOOL idle; + BOOL lrqHigh; + BOOL speaking; + INT32 fifoBytes[64]; + INT32 fifoHead; + INT32 fifoSize; + + //random number generator + INT32 random; +}; + +#endif diff --git a/arm9/source/emucore/SP0256_Registers.cpp b/arm9/source/emucore/SP0256_Registers.cpp new file mode 100644 index 0000000..a8072b2 --- /dev/null +++ b/arm9/source/emucore/SP0256_Registers.cpp @@ -0,0 +1,51 @@ + +#include "SP0256.h" +#include "SP0256_Registers.h" + +SP0256_Registers::SP0256_Registers() +: RAM(2, 0x0080, 0xFFFF, 0xFFFF) +{} + +void SP0256_Registers::init(SP0256* ms) +{ + this->ms = ms; +} + +void SP0256_Registers::poke(UINT16 location, UINT16 value) +{ + switch(location) { + //a poke of any value into $80 means that the SP0256 should + //start speaking + case 0x0080: + if (ms->lrqHigh) { + ms->lrqHigh = FALSE; + + ms->command = value & 0xFF; + + if (!ms->speaking) + ms->idle = FALSE; + } + break; + //$81 will reset the SP0256 or push an 8-bit value into the queue + case 0x0081: + if (value & 0x0400) { + ms->resetProcessor(); + } + else if (ms->fifoSize < FIFO_MAX_SIZE) { + ms->fifoBytes[(ms->fifoHead+ms->fifoSize) & 0x3F] = value; + ms->fifoSize++; + } + break; + } +} + +UINT16 SP0256_Registers::peek(UINT16 location) { + switch(location) { + case 0x0080: + return (ms->lrqHigh ? 0x8000 : 0); + case 0x0081: + default: + return (ms->fifoSize == FIFO_MAX_SIZE ? 0x8000 : 0); + } +} + diff --git a/arm9/source/emucore/SP0256_Registers.h b/arm9/source/emucore/SP0256_Registers.h new file mode 100644 index 0000000..44778bc --- /dev/null +++ b/arm9/source/emucore/SP0256_Registers.h @@ -0,0 +1,28 @@ + +#ifndef MICROSEQUENCER_REGISTERS_H +#define MICROSEQUENCER_REGISTERS_H + +#include "types.h" +#include "RAM.h" + +class SP0256; + +class SP0256_Registers : public RAM +{ + + friend class SP0256; + + public: + void reset() {} + + void poke(UINT16 location, UINT16 value); + UINT16 peek(UINT16 location); + + private: + SP0256_Registers(); + void init(SP0256* ms); + SP0256* ms; + +}; + +#endif diff --git a/arm9/source/emucore/SignalLine.h b/arm9/source/emucore/SignalLine.h new file mode 100644 index 0000000..c881d67 --- /dev/null +++ b/arm9/source/emucore/SignalLine.h @@ -0,0 +1,30 @@ + +#ifndef SIGNALLINE_H +#define SIGNALLINE_H + +#include "types.h" + +class Processor; + +class SignalLine +{ + public: + SignalLine() + { SignalLine(NULL, 0, NULL, 0); } + + SignalLine(Processor* pop, UINT8 pon, Processor* pip, UINT8 pin) + : pinOutProcessor(pop), + pinOutNum(pon), + pinInProcessor(pip), + pinInNum(pin), + isHigh(FALSE) { } + + Processor* pinOutProcessor; + UINT8 pinOutNum; + Processor* pinInProcessor; + UINT8 pinInNum; + BOOL isHigh; + +}; + +#endif diff --git a/arm9/source/emucore/VideoBus.cpp b/arm9/source/emucore/VideoBus.cpp new file mode 100644 index 0000000..5a01362 --- /dev/null +++ b/arm9/source/emucore/VideoBus.cpp @@ -0,0 +1,93 @@ + +#include +#include +#include "VideoBus.h" + +VideoBus::VideoBus() + : pixelBuffer(NULL), + pixelBufferSize(0), + pixelBufferRowSize(0), + pixelBufferWidth(0), + pixelBufferHeight(0), + videoProducerCount(0) +{ +} + +VideoBus::~VideoBus() +{ + if (pixelBuffer) { + delete[] pixelBuffer; + } + + for (UINT32 i = 0; i < videoProducerCount; i++) + videoProducers[i]->setPixelBuffer(NULL, 0); +} + +void VideoBus::addVideoProducer(VideoProducer* p) +{ + videoProducers[videoProducerCount] = p; + videoProducers[videoProducerCount]->setPixelBuffer(pixelBuffer, pixelBufferRowSize); + videoProducerCount++; +} + +void VideoBus::removeVideoProducer(VideoProducer* p) +{ + for (UINT32 i = 0; i < videoProducerCount; i++) { + if (videoProducers[i] == p) { + videoProducers[i]->setPixelBuffer(NULL, 0); + + for (UINT32 j = i; j < (videoProducerCount-1); j++) + videoProducers[j] = videoProducers[j+1]; + videoProducerCount--; + return; + } + } +} + +void VideoBus::removeAll() +{ + while (videoProducerCount) + removeVideoProducer(videoProducers[0]); +} + +void VideoBus::init(UINT32 width, UINT32 height) +{ + VideoBus::release(); + + pixelBufferWidth = width; + pixelBufferHeight = height; + pixelBufferRowSize = width * sizeof(UINT8); + pixelBufferSize = width * height * sizeof(UINT8); + pixelBuffer = new UINT8[width * height]; + printf("VideoBus::init(%d,%d)\n", width, height); + + if ( pixelBuffer ) { + memset(pixelBuffer, 0, pixelBufferSize); + } + + for (UINT32 i = 0; i < videoProducerCount; i++) + videoProducers[i]->setPixelBuffer(pixelBuffer, pixelBufferRowSize); +} + +void VideoBus::render() +{ + //tell each of the video producers that they can now output their + //video contents onto the video device + for (UINT32 i = 0; i < videoProducerCount; i++) + videoProducers[i]->render(); +} + +void VideoBus::release() +{ + if (pixelBuffer) { + for (UINT32 i = 0; i < videoProducerCount; i++) + videoProducers[i]->setPixelBuffer(NULL, 0); + + pixelBufferWidth = 0; + pixelBufferHeight = 0; + pixelBufferRowSize = 0; + pixelBufferSize = 0; + delete[] pixelBuffer; + pixelBuffer = NULL; + } +} diff --git a/arm9/source/emucore/VideoBus.h b/arm9/source/emucore/VideoBus.h new file mode 100644 index 0000000..e465806 --- /dev/null +++ b/arm9/source/emucore/VideoBus.h @@ -0,0 +1,37 @@ + +#ifndef VIDEOBUS_H +#define VIDEOBUS_H + +#include "types.h" +#include "VideoProducer.h" + +const INT32 MAX_VIDEO_PRODUCERS = 10; + +class VideoBus +{ + public: + VideoBus(); + virtual ~VideoBus(); + + void addVideoProducer(VideoProducer* ic); + void removeVideoProducer(VideoProducer* ic); + void removeAll(); + + virtual void init(UINT32 width, UINT32 height); + virtual void render(); + virtual void release(); + + protected: + UINT8* pixelBuffer; + UINT32 pixelBufferSize; + UINT32 pixelBufferRowSize; + UINT32 pixelBufferWidth; + UINT32 pixelBufferHeight; + + private: + VideoProducer* videoProducers[MAX_VIDEO_PRODUCERS]; + UINT32 videoProducerCount; + +}; + +#endif diff --git a/arm9/source/emucore/VideoProducer.h b/arm9/source/emucore/VideoProducer.h new file mode 100644 index 0000000..04a0605 --- /dev/null +++ b/arm9/source/emucore/VideoProducer.h @@ -0,0 +1,27 @@ + +#ifndef VIDEOPRODUCER_H +#define VIDEOPRODUCER_H + +/** + * This interface is implemented by any piece of hardware that renders graphic + * output. + */ +class VideoProducer +{ + public: + /** + * Tells the video producer to render its output. It should *not* flip + * the output onto the screen as that will be done by the video bus + * after all video producers have rendered their output to the back + * buffer. + * + * This function will be called once each time the Emulator indicates + * that it has entered vertical blank. + */ + + virtual void setPixelBuffer(UINT8* pixelBuffer, UINT32 rowSize) = 0; + + virtual void render() = 0; +}; + +#endif diff --git a/arm9/source/emucore/knowncarts.cfg b/arm9/source/emucore/knowncarts.cfg new file mode 100644 index 0000000..b7aa37f --- /dev/null +++ b/arm9/source/emucore/knowncarts.cfg @@ -0,0 +1,696 @@ +D7C78754:Info:4-TRIS (GPL):Joseph Zbiciak:2000 +D7C78754:ROM:5000:2000:16 + +B91488E2:Info:4-TRIS (GPL):Joseph Zbiciak:2001 +B91488E2:ROM:5000:2000:16 + +A60E25FC:Info:ABPA Backgammon:Mattel:1978 +A60E25FC:ROM:5000:1000:16 + +F8B1F2B7:Info:Advanced Dungeons and Dragons:Mattel:1982 +F8B1F2B7:ROM:5000:2000:16 + +16C3B62F:Info:Advanced Dungeons and Dragons - Treasure of Tarmin:Mattel:1982 +16C3B62F:ROM:5000:2000:16 + +11C3BCFA:Info:Adventure (AD&D - Cloudy Mountain):Mattel:1982 +11C3BCFA:ROM:5000:2000:16 + +2C668249:Info:Air Strike:Mattel:1982 +2C668249:ROM:5000:1000:16 + +B45633CF:Info:All-Stars Major League Baseball:Mattel:1980 +B45633CF:ROM:5000:2000:16 +B45633CF:ROM:D000:1000:16 + +6F91FBC1:Info:Armor Battle:Mattel:1978 +6F91FBC1:ROM:5000:1000:16 + +00BE8BBA:Info:Astrosmash - Meteor:Mattel:1981 +00BE8BBA:ROM:5000:1000:16 + +FAB2992C:Info:Astrosmash:Mattel:1981 +FAB2992C:ROM:5000:1000:16 + +13FF363C:Info:Atlantis:Imagic:1981 +13FF363C:ROM:4800:2000:16 + +B35C1101:Info:Auto Racing:Mattel:1979 +B35C1101:ROM:5000:1000:16 + +8AD19AB3:Info:B-17 Bomber:Mattel:1981 +8AD19AB3:ROM:5000:2000:16 +8AD19AB3:ROM:D000:1000:16 +8AD19AB3:Peripheral:Intellivoice:Optional + +DAB36628:Info:Baseball:Mattel:1978 +DAB36628:ROM:5000:1000:16 + +EAF650CC:Info:BeamRider:Activision:1983 +EAF650CC:ROM:5000:2000:16 + +C047D487:Info:Beauty and the Beast:Imagic:1982 +C047D487:ROM:4800:2000:16 + +B03F739B:Info:Blockade Runner:Interphase:1983 +B03F739B:ROM:5000:2000:16 + +4728C3BD:Info:Blow Out:Mattel:1983 +4728C3BD:ROM:5000:1000:16 + +515E1D7E:Info:Body Slam Super Pro Wrestling:Mattel:1988 +515E1D7E:ROM:5000:2000:16 +515E1D7E:ROM:9000:2000:16 + +32697B72:Info:Bomb Squad:Mattel:1982 +32697B72:ROM:5000:2000:16 +32697B72:ROM:D000:1000:16 +32697B72:Peripheral:Intellivoice:Optional + +18E08520:Info:Bouncing Pixels (GPL):JRMZ Electronics:1999 +18E08520:ROM:5000:0202:16 + +AB87C16F:Info:Boxing:Mattel:1980 +AB87C16F:ROM:5000:1000:16 + +9F85015B:Info:Brickout!:Mattel:1981 +9F85015B:ROM:5000:1000:16 + +999CCEED:Info:Bump 'N' Jump:Mattel:1982-83 +999CCEED:ROM:5000:2000:16 +999CCEED:ROM:D000:1000:16 +999CCEED:ROM:F000:1000:16 + +43806375:Info:BurgerTime!:Mattel:1982 +43806375:ROM:5000:2000:16 + +C92BAAE8:Info:BurgerTime! - New Levels Hack:David Harley:2002 +C92BAAE8:ROM:5000:2000:16 + +FA492BBD:Info:Buzz Bombers:Mattel:1982 +FA492BBD:ROM:5000:2000:16 + +43870908:Info:Carnival:Coleco:1982 +43870908:ROM:5000:1000:16 + +D5363B8C:Info:Centipede:Atarisoft:1983 +D5363B8C:ROM:6000:2000:16 + +4CC46A04:Info:Championship Tennis:Nice Ideas:1985 +4CC46A04:ROM:5000:2000:16 +4CC46A04:ROM:D000:2000:16 + +36E1D858:Info:Checkers:Mattel:1979 +36E1D858:ROM:5000:1000:16 + +0BF464C6:Info:Chip Shot Super Pro Golf:Mattel:1987 +0BF464C6:ROM:5000:2000:16 +0BF464C6:ROM:9000:2000:16 + +47FDD8A8:Info:Choplifter:Mattel:1983 +47FDD8A8:ROM:5000:2000:16 +47FDD8A8:ROM:D000:2000:16 + +3289C8BA:Info:Commando:INTV:1987 +3289C8BA:ROM:5000:2000:16 +3289C8BA:ROM:9000:2000:16 + +4B23A757:Info:Congo Bongo:Sega:1983 +4B23A757:ROM:5000:3000:16 + +E1EE408F:Info:Crazy Clones:Mattel:1981 +E1EE408F:ROM:5000:1000:16 + +CDC14ED8:Info:Deadly Dogs!:Unknown:1987 +CDC14ED8:ROM:5000:1000:16 + +6802B191:Info:Deep Pockets - Super Pro Pool and Billiards:Realtime:1990 +6802B191:ROM:5000:2000:16 +6802B191:ROM:9000:2000:16 + +D8F99AA2:Info:Defender:Atarisoft:1983 +D8F99AA2:ROM:5000:3000:16 + +5E6A8CD8:Info:Demon Attack:Imagic:1982 +5E6A8CD8:ROM:4800:2000:16 + +159AF7F7:Info:Dig Dug:INTV:1987 +159AF7F7:ROM:5000:3000:16 +159AF7F7:ROM:9000:1000:16 + +13EE56F1:Info:Diner:INTV:1987 +13EE56F1:ROM:5000:2000:16 +13EE56F1:ROM:9000:2000:16 + +C30F61C0:Info:Donkey Kong:Coleco:1982 +C30F61C0:ROM:5000:1000:16 + +6DF61A9F:Info:Donkey Kong Jr:Coleco:1982 +6DF61A9F:ROM:5000:2000:16 + +84BEDCC1:Info:Dracula:Imagic:1982 +84BEDCC1:ROM:5000:2000:16 + +AF8718A1:Info:Dragonfire:Imagic:1982 +AF8718A1:ROM:5000:1000:16 + +3B99B889:Info:Dreadnaught Factor, The:Activision:1983 +3B99B889:ROM:5000:2000:16 + +BF4D0E9B:Info:Dreadnaught Factor, The (Prototype):Activision:1983 +BF4D0E9B:ROM:5000:2000:16 + +20ACE89D:Info:Easter Eggs:Mattel:1981 +20ACE89D:ROM:5000:1000:16 + +54A3FC11:Info:Electric Company - Math Fun:CTW:1978 +54A3FC11:ROM:5000:1000:16 + +C9EAACAB:Info:Electric Company - Word Fun:CTW:1980 +C9EAACAB:ROM:5000:1000:16 + +4221EDE7:Info:Fathom:Imagic:1983 +4221EDE7:ROM:5000:2000:16 + +37222762:Info:Frog Bog:Mattel:1982 +37222762:ROM:5000:1000:16 + +D27495E9:Info:Frogger:Parker Bros:1983 +D27495E9:ROM:5000:1000:16 + +DBCA82C5:Info:Go For The Gold:Mattel:1981 +DBCA82C5:ROM:5000:2000:16 + +291AC826:Info:Grid Shock:Mattel:1982 +291AC826:ROM:5000:1000:16 + +E573863A:Info:Groovy! (GPL):JRMZ Electronics:1999 +E573863A:ROM:5000:1E48:16 + +4B8C5932:Info:Happy Trails:Activision:1983 +4B8C5932:ROM:5000:1000:16 + +120B53A9:Info:Happy Trails (Overdump):Activision:1983 +120B53A9:ROM:5000:1000:16 + +B6A3D4DE:Info:Hard Hat:Mattel:1979 +B6A3D4DE:ROM:5000:2000:16 + +B5C7F25D:Info:Horse Racing:Mattel:1980 +B5C7F25D:ROM:5000:1000:16 + +FF83FF80:Info:Hover Force:Mattel:1986 +FF83FF80:ROM:5000:2000:16 +FF83FF80:ROM:9000:3000:16 +FF83FF80:ROM:D000:1000:16 + +A3147630:Info:Hypnotic Lights:Mattel:1981 +A3147630:ROM:5000:1000:16 + +4F3E3F69:Info:Ice Trek:Imagic:1983 +4F3E3F69:ROM:5000:2000:16 + +02919024:Info:Intellivision - Intelligent Television Demo:Mattel:1978 +02919024:ROM:5000:2000:16 + +C83EEA4C:Info:Intellivision Test Cartridge and Baseball:Mattel:1978 +C83EEA4C:ROM:5000:1000:16 +C83EEA4C:ROM:7000:1000:16 + +985A78ED:Info:IntyOS 0.2 Alpha:Arnauld Chevallier:2003 +985A78ED:ROM:5000:1000:16 +985A78ED:RAM:D000:1000:16 +985A78ED:RAM:F000:1000:16 + +EE5F1BE2:Info:Jetsons, The - Ways With Words:Mattel:1983 +EE5F1BE2:ROM:5000:2000:16 +EE5F1BE2:ROM:D000:1000:16 +EE5F1BE2:Peripheral:ECS:Required + +4422868E:Info:King of the Mountain:Mattel:1982 +4422868E:ROM:5000:2000:16 +4422868E:ROM:D000:2000:16 + +8C9819A2:Info:Kool-Aid Man:Mattel:1983 +8C9819A2:ROM:5000:2000:16 + +A6840736:Info:Lady Bug:Coleco:1983 +A6840736:ROM:5000:2000:16 + +3825C25B:Info:Land Battle:Mattel:1982 +3825C25B:ROM:5000:2000:16 +3825C25B:RAM:D000:400:8 + +604611C0:Info:Las Vegas Blackjack and Poker:Mattel:1979 +604611C0:ROM:5000:1000:16 + +48D74D3C:Info:Las Vegas Roulette:Mattel:1979 +48D74D3C:ROM:5000:1000:16 + +19360442:Info:League of Light (Prototype Alt1):Activision:1983 +19360442:ROM:5000:2000:16 + +75EE64F6:Info:League of Light (Prototype Alt2):Activision:1983 +75EE64F6:ROM:5000:2000:16 + +B4287B95:Info:League of Light (Prototype):Activision:1983 +B4287B95:ROM:5000:2000:16 + +2C5FD5FA:Info:Learning Fun I - Math Master Factor Fun:INTV:1987 +2C5FD5FA:ROM:5000:2000:16 + +632F6ADF:Info:Learning Fun II - Word Wizard Memory Fun:INTV:1987 +632F6ADF:ROM:5000:2000:16 + +E00D1399:Info:Lock 'N' Chase:Mattel:1982 +E00D1399:ROM:5000:2000:16 + +6B6E80EE:Info:Loco-Motion:Mattel:1982 +6B6E80EE:ROM:5000:2000:16 + +F3B0C759:Info:Magic Carousel:Mattel:1982 +F3B0C759:ROM:5000:2000:16 +F3B0C759:ROM:D000:2000:16 + +573B9B6D:Info:Masters of the Universe - The Power of He-Man!:Mattel:1983 +573B9B6D:ROM:5000:2000:16 +573B9B6D:ROM:D000:1000:16 +573B9B6D:ROM:F000:1000:16 + +7D0F8162:Info:Maze Demo #1 (GPL):JRMZ Electronics:2000 +7D0F8162:ROM:5000:016E:16 + +2138DAD4:Info:Maze Demo #2 (GPL):JRMZ Electronics:2000 +2138DAD4:ROM:5000:0175:16 + +FF68AA22:Info:Melody Blaster:Mattel:1983 +FF68AA22:ROM:5000:2000:16 +FF68AA22:ROM:D000:1000:16 +FF68AA22:Peripheral:ECS:Required + +E00E23E7:Info:Mickey's Hello World:Mickey:2000 +E00E23E7:ROM:5000:006F:16 + +E806AD91:Info:Microsurgeon:Imagic:1982 +E806AD91:ROM:4800:2000:16 + +94096229:Info:Minehunter:Ryan Kinnen:2004 +94096229:ROM:5000:2000:16 + +9D57498F:Info:Mind Strike!:Mattel:1982 +9D57498F:ROM:5000:2000:16 +9D57498F:ROM:D000:1000:16 +9D57498F:Peripheral:ECS:Required + +6746607B:Info:Minotaur V1.1:Mattel:1981 +6746607B:ROM:5000:2000:16 + +5A4CE519:Info:Minotaur V2:Mattel:1981 +5A4CE519:ROM:5000:2000:16 + +BD731E3C:Info:Minotaur:Mattel:1981 +BD731E3C:ROM:5000:2000:16 + +2F9C93FC:Info:Minotaur (Treasure of Tarmin hack):Mattel:1981 +2F9C93FC:ROM:5000:2000:16 + +11FB9974:Info:Mission X:Mattel:1982 +11FB9974:ROM:5000:2000:16 + +5F6E1AF6:Info:Motocross:Mattel:1982 +5F6E1AF6:ROM:5000:2000:16 + +6B5EA9C4:Info:Mountain Madness Super Pro Skiing:INTV:1987 +6B5EA9C4:ROM:5000:2000:16 + +598662F2:Info:Mouse Trap:Coleco:1982 +598662F2:ROM:5000:1000:16 + +0B50A367:Info:Mr. Basic Meets Bits 'N Bytes (Bad Dump):Mattel:1983 +0B50A367:ROM:5000:2000:16 +0B50A367:ROM:D000:1000:16 +0B50A367:Peripheral:ECS:Required + +BEF0B0C7:Info:Mr. Basic Meets Bits 'N Bytes:Mattel:1983 +BEF0B0C7:ROM:5000:2000:16 +BEF0B0C7:ROM:D000:1000:16 +BEF0B0C7:Peripheral:ECS:Required + +DBAB54CA:Info:NASL Soccer:Mattel:1979 +DBAB54CA:ROM:5000:1000:16 + +81E7FB8C:Info:NBA Basketball:Mattel:1978 +81E7FB8C:ROM:5000:1000:16 + +4B91CF16:Info:NFL Football:Mattel:1978 +4B91CF16:ROM:5000:1000:16 + +76564A13:Info:NHL Hockey:Mattel:1979 +76564A13:ROM:5000:1000:16 + +7334CD44:Info:Night Stalker:Mattel:1982 +7334CD44:ROM:5000:1000:16 + +5EE2CC2A:Info:Nova Blast:Imagic:1983 +5EE2CC2A:ROM:5000:2000:16 + +E5D1A8D2:Info:Number Jumble:Mattel:1983 +E5D1A8D2:ROM:5000:2000:16 +E5D1A8D2:ROM:D000:1000:16 +E5D1A8D2:ROM:F000:1000:16 + +A21C31C3:Info:Pac-Man:Atarisoft:1983 +A21C31C3:ROM:5000:3000:16 + +6E4E8EB4:Info:Pac-Man:INTV:1983 +6E4E8EB4:ROM:5000:3000:16 + +169E3584:Info:PBA Bowling:Mattel:1980 +169E3584:ROM:5000:1000:16 + +FF87FAEC:Info:PGA Golf:Mattel:1979 +FF87FAEC:ROM:5000:1000:16 + +D7C5849C:Info:Pinball:Mattel:1981 +D7C5849C:ROM:5000:2000:16 +D7C5849C:ROM:D000:1000:16 + +9C75EFCC:Info:Pitfall!:Activision:1982 +9C75EFCC:ROM:5000:1000:16 + +BB939881:Info:Pole Position:INTV:1986 +BB939881:ROM:5000:2000:16 +BB939881:ROM:9000:2000:16 + +A982E8D5:Info:Pong:Unknown:1999 +A982E8D5:ROM:5000:0800:16 + +C51464E0:Info:Popeye:Parker Bros:1983 +C51464E0:ROM:5000:2000:16 + +D8C9856A:Info:Q-bert:Parker Bros:1983 +D8C9856A:ROM:5000:2000:16 + +C7BB1B0E:Info:Reversi:Mattel:1984 +C7BB1B0E:ROM:5000:1000:16 + +8910C37A:Info:River Raid:Activision:1982-83 +8910C37A:ROM:5000:2000:16 + +95466AD3:Info:River Raid V1 (Prototype):Activision:1982-83 +95466AD3:ROM:5000:2000:16 + +1682D0B4:Info:Robot Rubble V1 (Prototype) (Overdump):Activision:1983 +1682D0B4:ROM:5000:2000:16 + +7473916D:Info:Robot Rubble V1 (Prototype):Activision:1983 +7473916D:ROM:5000:2000:16 + +A5E28783:Info:Robot Rubble V2 (Prototype):Activision:1983 +A5E28783:ROM:5000:2000:16 + +243B0812:Info:Robot Rubble V3 (Prototype):Activision:1983 +243B0812:ROM:5000:2000:16 + +DCF4B15D:Info:Royal Dealer:Mattel:1981 +DCF4B15D:ROM:5000:2000:16 + +47AA7977:Info:Safecracker:Imagic:1983 +47AA7977:ROM:5000:2000:16 + +6E0882E7:Info:SameGame and Robots:IntelligentVision:2005 +6E0882E7:ROM:5000:2000:16 +6E0882E7:ROM:D000:1000:16 +6E0882E7:ROM:F000:1000:16 + +12BA58D1:Info:SameGame and Robots (Original):Michael J Hayes:2004 +12BA58D1:ROM:5000:2000:16 +12BA58D1:ROM:D000:1000:16 +12BA58D1:ROM:F000:1000:16 + +E221808C:Info:Santa's Helper:Mattel:1983 +E221808C:ROM:5000:1000:16 + +E9E3F60D:Info:Scooby Doo's Maze Chase:Mattel/Hanna-Barbera:1983 +E9E3F60D:ROM:5000:2000:16 +E9E3F60D:Peripheral:ECS:Required + +99AE29A9:Info:Sea Battle:Mattel:1980 +99AE29A9:ROM:5000:1000:16 + +E0F0D3DA:Info:Sewer Sam:Interphase:1983 +E0F0D3DA:ROM:5000:2000:16 + +A610406E:Info:Shape Escape:John Doherty:2005 +A610406E:ROM:5000:2000:16 + +2A4C761D:Info:Shark! Shark!:Mattel:1982 +2A4C761D:ROM:5000:2000:16 + +FF7CB79E:Info:Sharp Shot:Mattel:1982 +FF7CB79E:ROM:5000:1000:16 + +800B572F:Info:Slam Dunk Super Pro Basketball:INTV:1987 +800B572F:ROM:5000:2000:16 +800B572F:ROM:9000:2000:16 + +BA68FF28:Info:Slap Shot Super Pro Hockey:INTV:1987 +BA68FF28:ROM:5000:2000:16 + +8F959A6E:Info:Snafu:Mattel:1981 +8F959A6E:ROM:5000:1000:16 + +72C2A343:Info:Song Player - Back in the USSR (GPL):Joseph Zbiciak:1999 +72C2A343:ROM:5000:2000:16 +72C2A343:ROM:9000:1241:16 + +5E3130E1:Info:Song Player - Copacabana (GPL):Joseph Zbiciak:1999 +5E3130E1:ROM:5000:2000:16 +5E3130E1:ROM:9000:088F:16 +5E3130E1:Peripheral:ECS:Required + +C91B1D79:Info:Song Player - Creep (GPL):Joseph Zbiciak:1999 +C91B1D79:ROM:5000:1081:16 +C91B1D79:Peripheral:ECS:Required + +82D2E346:Info:Song Player - Nut March (GPL):Joseph Zbiciak:1999 +82D2E346:ROM:5000:1B12:16 +82D2E346:Peripheral:ECS:Required + +FCF06A8B:Info:Song Player - Nut Reed (GPL):Joseph Zbiciak:1999 +FCF06A8B:ROM:5000:145E:16 +FCF06A8B:Peripheral:ECS:Required + +5914220A:Info:Song Player - Nut Trep (GPL):Joseph Zbiciak:1999 +5914220A:ROM:5000:0DC8:16 +5914220A:Peripheral:ECS:Required + +71F5BE4E:Info:Song Player - Nut Waltz (GPL):Joseph Zbiciak:1999 +71F5BE4E:ROM:5000:2000:16 +71F5BE4E:ROM:9000:157C:16 +71F5BE4E:Peripheral:ECS:Required + +437C0E2D:Info:Song Player - Secret Agent Man (GPL):Joseph Zbiciak:1999 +437C0E2D:ROM:5000:2000:16 +437C0E2D:ROM:9000:15A4:16 +437C0E2D:Peripheral:ECS:Required + +DA5D77EA:Info:Song Player - Take on Me 3 (GPL):Joseph Zbiciak:1999 +DA5D77EA:ROM:5000:12FD:16 +DA5D77EA:Peripheral:ECS:Required + +10D721CA:Info:Song Player - Take on Me 6 (GPL):Joseph Zbiciak:1999 +10D721CA:ROM:5000:1500:16 +10D721CA:Peripheral:ECS:Required + +CC6F4F90:Info:Song Player - Too Sexy (GPL):Joseph Zbiciak:1999 +CC6F4F90:ROM:5000:1AA7:16 +CC6F4F90:Peripheral:ECS:Required + +E8B8EBA5:Info:Space Armada:Mattel:1981 +E8B8EBA5:ROM:5000:1000:16 + +F95504E0:Info:Space Battle:Mattel:1979 +F95504E0:ROM:5000:1000:16 + +F8EF3E5A:Info:Space Cadet:Mattel:1982 +F8EF3E5A:ROM:5000:2000:16 + +39D3B895:Info:Space Hawk:Mattel:1981 +39D3B895:ROM:5000:1000:16 + +E98B9163:Info:Space Shuttle:Mattel:1983 +E98B9163:ROM:5000:2000:16 +E98B9163:ROM:D000:3000:16 +E98B9163:Peripheral:Intellivoice:Optional + +3784DC52:Info:Space Spartans:Mattel:1981 +3784DC52:ROM:5000:2000:16 +3784DC52:Peripheral:Intellivoice:Optional + +A95021FC:Info:Spiker! Super Pro Volleyball:INTV:1988 +A95021FC:ROM:5000:2000:16 +A95021FC:ROM:9000:2000:16 + +B745C1CA:Info:Stadium Mud Buggies:INTV:1988 +B745C1CA:ROM:5000:2000:16 +B745C1CA:ROM:9000:2000:16 + +2DEACD15:Info:Stampede:Activision:1982 +2DEACD15:ROM:5000:1000:16 + +72E11FCA:Info:Star Strike:Mattel:1981 +72E11FCA:ROM:5000:1000:16 + +D5B0135A:Info:Star Wars - The Empire Strikes Back:Parker Bros:1983 +D5B0135A:ROM:5000:1000:16 + +A03EDF73:Info:Stack Em:Arnauld Chevallier:2004 +A03EDF73:ROM:4800:2000:16 + +66D396C0:Info:Stonix:Arnauld Chevallier:2004 +66D396C0:ROM:5000:2000:16 +66D396C0:ROM:D000:1000:16 +66D396C0:ROM:F000:1000:16 + +B119027D:Info:Stonix Beta 1.2:Arnauld Chevallier:2003 +B119027D:ROM:5000:2000:16 + +4830F720:Info:Street:Mattel:1981 +4830F720:ROM:5000:1000:16 + +3D9949EA:Info:Sub Hunt:Mattel:1981 +3D9949EA:ROM:5000:2000:16 + +8F7D3069:Info:Super Cobra:Konami:1983 +8F7D3069:ROM:5000:2000:16 + +BAB638F2:Info:Super Masters!:Mattel:1982 +BAB638F2:ROM:5000:2000:16 + +16BFB8EB:Info:Super Pro Decathlon:INTV:1978 +16BFB8EB:ROM:5000:2000:16 +16BFB8EB:ROM:9000:2000:16 + +32076E9D:Info:Super Pro Football:INTV:1986 +32076E9D:ROM:5000:2000:16 +32076E9D:ROM:9000:2000:16 + +51B82EB7:Info:Super Soccer:Mattel:1983 +51B82EB7:ROM:5000:2000:16 +51B82EB7:ROM:D000:1000:16 +51B82EB7:ROM:F000:1000:16 + +15E88FCE:Info:Swords and Serpents:Imagic:1982 +15E88FCE:ROM:5000:2000:16 + +1F584A69:Info:Takeover:Mattel:1982 +1F584A69:ROM:5000:1000:16 + +03E9E62E:Info:Tennis:Mattel:1980 +03E9E62E:ROM:5000:1000:16 + +D43FD410:Info:Tetris (GPL):Joseph Zbiciak:2000 +D43FD410:ROM:5000:2000:16 + +F3DF94E0:Info:Thin Ice:Mattel:1983 +F3DF94E0:ROM:5000:2000:16 +F3DF94E0:ROM:D000:1000:16 +F3DF94E0:ROM:F000:1000:16 + +D6495910:Info:Thin Ice (Prototype):Mattel:1983 +D6495910:ROM:5000:2000:16 + +C1F1CA74:Info:Thunder Castle:Mattel:1982 +C1F1CA74:ROM:5000:2000:16 +C1F1CA74:ROM:D000:1000:16 +C1F1CA74:ROM:F000:1000:16 + +D1D352A0:Info:Tower of Doom:Mattel:1986 +D1D352A0:ROM:5000:2000:16 +D1D352A0:ROM:9000:2000:16 +D1D352A0:ROM:D000:1000:16 +D1D352A0:ROM:F000:1000:16 + +1AC989E2:Info:Triple Action:Mattel:1981 +1AC989E2:ROM:5000:1000:16 + +095638C0:Info:Triple Challenge:INTV:1986 +095638C0:ROM:5000:2000:16 +095638C0:ROM:9000:2000:16 +095638C0:ROM:C000:0800:16 +095638C0:ROM:D000:1000:16 + +7A558CF5:Info:Tron Maze-A-Tron:Mattel:1981 +7A558CF5:ROM:5000:2000:16 + +CA447BBD:Info:Tron Deadly Discs:Mattel:1981 +CA447BBD:ROM:5000:1000:16 + +07FB9435:Info:Tron Solar Sailer:Mattel:1982 +07FB9435:ROM:5000:2000:16 +07FB9435:ROM:D000:1000:16 +07FB9435:Peripheral:Intellivoice:Optional + +6F23A741:Info:Tropical Trouble:Imagic:1982 +6F23A741:ROM:5000:2000:16 + +734F3260:Info:Truckin':Imagic:1983 +734F3260:ROM:5000:2000:16 + +275F3512:Info:Turbo:Coleco:1983 +275F3512:ROM:5000:2000:16 + +6FA698B3:Info:Tutankham:Parker Bros:1983 +6FA698B3:ROM:5000:2000:16 + +F093E801:Info:U.S. Ski Team Skiing:Mattel:1980 +F093E801:ROM:5000:1000:16 + +752FD927:Info:USCF Chess:Mattel:1981 +752FD927:ROM:5000:2000:16 +752FD927:RAM:D000:400:8 + +F9E0789E:Info:Utopia:Mattel:1981 +F9E0789E:ROM:5000:1000:16 + +A4A20354:Info:Vectron:Mattel:1982 +A4A20354:ROM:5000:2000:16 +A4A20354:ROM:D000:1000:16 + +6EFA67B2:Info:Venture:Coleco:1982 +6EFA67B2:ROM:5000:2000:16 + +F1ED7D27:Info:White Water!:Imagic:1983 +F1ED7D27:ROM:5000:2000:16 + +15D9D27A:Info:World Cup Football:Nice Ideas:1985 +15D9D27A:ROM:5000:2000:16 +15D9D27A:ROM:D000:1000:16 +15D9D27A:ROM:F000:1000:16 + +C2063C08:Info:World Series Major League Baseball:Mattel:1983 +C2063C08:ROM:5000:2000:16 +C2063C08:ROM:D000:1000:16 +C2063C08:ROM:E000:1000:16:EFFF,FFF0=EA50:000F=0000 +C2063C08:ROM:F000:1000:16:FFFF,FFF0=FA50:000F=0000 +C2063C08:ROM:F000:1000:16:FFFF,FFF0=FA50:000F=0001 +C2063C08:Peripheral:Intellivoice:Optional +C2063C08:Peripheral:ECS:Required + +A12C27E1:Info:World Series Major League Baseball (Bad Dump):Mattel:1983 +A12C27E1:ROM:5000:2000:16 +A12C27E1:ROM:D000:1000:16 +A12C27E1:ROM:E000:1000:16:EFFF,FFF0=EA50:000F=0000 +A12C27E1:ROM:F000:1000:16:FFFF,FFF0=FA50:000F=0000 +A12C27E1:Peripheral:Intellivoice:Optional +A12C27E1:Peripheral:ECS:Required + +24B667B9:Info:Worm Whomper:Activision:1983 +24B667B9:ROM:5000:1000:16 + +15C65DC5:Info:Zaxxon:Coleco:1982 +15C65DC5:ROM:5000:2000:16 + +D89AEC27:Info:Zombie Marbles:John Doherty:2004 +D89AEC27:ROM:5000:2000:16 diff --git a/arm9/source/emucore/ripxbf.c b/arm9/source/emucore/ripxbf.c new file mode 100644 index 0000000..071c802 --- /dev/null +++ b/arm9/source/emucore/ripxbf.c @@ -0,0 +1,144 @@ + +#include +#include "ripxbf.h" + +UINT64 freadInt(FILE* file, UINT32 size) +{ + unsigned int i; + unsigned int ret = 0; + for (i = 0; i < size; i++) { + ret <<= 8; + ret |= fgetc(file); + } + return ret; +} + +UINT16 freadUINT16(FILE* file) +{ + return (UINT16)freadInt(file, 2); +} + +UINT32 freadUINT32(FILE* file) +{ + return (UINT32)freadInt(file, 4); +} + +UINT64 freadUINT64(FILE* file) +{ + return freadInt(file, 8); +} + +void freadString(FILE* file, CHAR* out, UINT32 maxLength) +{ + unsigned int i; + unsigned char next; + unsigned int completeLength = freadUINT32(file); + if (completeLength+1 < maxLength) + maxLength = completeLength+1; + + for (i = 0; i < completeLength; i++) { + next = (unsigned char)fgetc(file); + if (i < (maxLength-1)) + out[i] = next; + } + out[maxLength-1] = '\0'; +} + +void fwriteUINT16(FILE* file, UINT16 num) +{ + fputc((num>>8)&0xFF, file); + fputc(num&0xFF, file); +} + +void fwriteUINT32(FILE* file, UINT32 num) +{ + fputc((num>>24)&0xFF, file); + fputc((num>>16)&0xFF, file); + fputc((num>>8)&0xFF, file); + fputc(num&0xFF, file); +} + +void fwriteUINT64(FILE* file, UINT64 num) +{ + fputc((int)(num>>56)&0xFF, file); + fputc((int)(num>>48)&0xFF, file); + fputc((int)(num>>40)&0xFF, file); + fputc((int)(num>>32)&0xFF, file); + fputc((int)(num>>24)&0xFF, file); + fputc((int)(num>>16)&0xFF, file); + fputc((int)(num>>8)&0xFF, file); + fputc((int)num&0xFF, file); +} + +void fwriteString(FILE* file, const CHAR* s) +{ + int i; + int length = (int)strlen(s); + fwriteUINT32(file, length); + for (i = 0; i < length; i++) + fputc(s[i], file); +} + +/* +RipSystem SYSTEMS[] = +{ + { ID_SYSTEM_INTELLIVISION, + "Intellivision", + 2, 2, + { { "Master Component", + { { "Executive ROM", + { { "Original Executive ROM", 0xCBCE86F7 }, + { "Intellivision 2 Executive ROM", 0 }, + { "Sears Executive ROM", 0 }, + { "GPL Executive ROM", 0 }, + 0, + },}, + { "Graphics ROM", + { { "Original GROM", 0x683A4158 }, + { "GPL GROM", 0 }, + 0, + },}, + 0, + },}, + { "Entertainment Computer System", + { { "ECS BIOS", + { { "Original ECS BIOS", 0xEA790A06 }, + },}, + 0, + },}, + { "Intellivoice", + { { "Intellivoice BIOS", + { { "Original Intellivoice BIOS", 0x0DE7579D }, + 0, + },}, + 0, + },}, + 0, + },}, + { ID_SYSTEM_ATARI5200, + "Atari 5200", + 2, 1, + { { "Main Console", + { { "BIOS", + { { "Original BIOS", 0x4248D3E3 }, + 0, + },}, + 0, + },}, + 0, + },}, + 0, +}; + + +RipSystem* getSystem(UINT32 systemID) +{ + int i = 0; + while (*((int*)(&SYSTEMS[i])) != 0) { + if (SYSTEMS[i].systemID == systemID) + return &SYSTEMS[i]; + i++; + } + return NULL; +} +*/ diff --git a/arm9/source/emucore/ripxbf.h b/arm9/source/emucore/ripxbf.h new file mode 100644 index 0000000..5e8b962 --- /dev/null +++ b/arm9/source/emucore/ripxbf.h @@ -0,0 +1,120 @@ +/** + * This file contains a list of declarations useful for working with + * files in the XBF format. + * + * Author: Kyle Davis + */ + +#include +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//compatibility flags +typedef enum _BiosCompatibility +{ + BIOS_PREFERRED = 0, + BIOS_COMPATIBLE = 1, + BIOS_INCOMPATIBLE = 2 +} BiosCompatibility; + +typedef enum _PeripheralCompatibility +{ + PERIPH_REQUIRED = 0, + PERIPH_OPTIONAL = 1, + PERIPH_COMPATIBLE = 2, + PERIPH_INCOMPATIBLE = 3 +} PeripheralCompatibility; + +UINT64 freadInt(FILE*, UINT32 length); +UINT16 freadUINT16(FILE*); +UINT32 freadUINT32(FILE*); +UINT64 freadUINT64(FILE*); +void freadString(FILE*, CHAR* s, UINT32 maxLength); +void fwriteUINT16(FILE*, UINT16); +void fwriteUINT32(FILE*, UINT32); +void fwriteUINT64(FILE*, UINT64); +void fwriteString(FILE*, const CHAR*); + +//the RIP magic number +#define RIP_MAGIC_NUMBER 0x3F383A347651B5DAll +#define RIP_MAJOR_REVISION 1 +#define RIP_MINOR_REVISION 0 + +//record IDs +#define ID_HEADER_RECORD 0xFC41466E +#define ID_BIOS_COMPAT_RECORD 0xC183BF3C +#define ID_NAME_RECORD 0x5ECC1C53 +#define ID_PERIPH_COMPAT_RECORD 0xAE4CEDB7 +#define ID_PRODUCER_RECORD 0x86596370 +#define ID_YEAR_RECORD 0x9199748D +#define ID_RAM_RECORD 0xCF1DC943 +#define ID_ROM_RECORD 0xA365AF69 + +//emulator IDs +#define ID_EMULATOR_BLISS 0xC183BF3C + +//system IDs +#define ID_SYSTEM_ATARI5200 0xE4453A0B +#define ID_SYSTEM_INTELLIVISION 0x4AC771F8 +#define ID_PERIPH_ECS 0x143D9A97 +#define ID_PERIPH_INTELLIVOICE 0x6EFF540A + +//bios numbers +#define A5200_P_MASTER_COMPONENT 0 +#define A5200_BT_BIOS 0 +#define A5200_B_ORIGINAL_BIOS 0 + +#define INTV_P_MASTER_COMPONENT 0 +#define INTV_BT_EXEC 0 +#define INTV_B_ORIGINAL_EXEC 0 +#define INTV_B_INTY2_EXEC 1 +#define INTV_B_SEARS_EXEC 2 +#define INTV_B_GPL_EXEC 3 +#define INTV_BT_GROM 1 +#define INTV_B_ORIGINAL_GROM 0 +#define INTV_B_GPL_GROM 1 +#define INTV_P_ECS 1 +#define INTV_BT_ECS_BIOS 0 +#define INTV_B_ORIGINAL_ECS_BIOS 0 +#define INTV_P_INTELLIVOICE 2 +#define INTV_BT_IVOICE_BIOS 0 +#define INTV_B_ORIGINAL_IVOICE_BIOS 0 + +/* +typedef struct _RipBios +{ + CHAR* biosName; + UINT32 biosCRC32; +} RipBios; + +typedef struct _RipBiosType +{ + CHAR* biosTypeName; + RipBios bioses[16]; +} RipBiosType; + +typedef struct _RipPeripheral +{ + CHAR* peripheralName; + RipBiosType biosTypes[16]; +} RipPeripheral; + +typedef struct _RipSystem +{ + UINT32 systemID; + CHAR* systemName; + UINT8 addressByteWidth; + UINT8 valueByteWidth; + RipPeripheral peripherals[16]; +} RipSystem; + +RipSystem* getSystem(UINT32 systemID); +*/ + +#ifdef __cplusplus +} +#endif + diff --git a/arm9/source/emucore/types.h b/arm9/source/emucore/types.h new file mode 100644 index 0000000..0c916d1 --- /dev/null +++ b/arm9/source/emucore/types.h @@ -0,0 +1,174 @@ + +#ifndef TYPES_H +#define TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int debug1, debug2; + +//primitive types +typedef signed char INT8; +typedef unsigned char UINT8; +typedef signed short INT16; +typedef unsigned short UINT16; +typedef signed int INT32; +typedef unsigned int UINT32; + +// There is no ANSI standard for 64 bit integers :( +#ifdef _MSC_VER +typedef __int64 INT64; // MS VC++ +typedef unsigned __int64 UINT64; // MS VC++ +#else +typedef signed long long int INT64; // C99 C++11 +typedef unsigned long long int UINT64; // C99 C++11 +#endif + +typedef char CHAR; + +#if !defined(BOOL) +#if defined(__MACH__) +typedef signed char BOOL; +#else +typedef int BOOL; +#endif +#endif + +#if !defined(TRUE) +#define TRUE (1) +#endif + +#if !defined(FALSE) +#define FALSE (0) +#endif + +#if !defined(NULL) +#if defined(__cplusplus) +#define NULL (0) +#else +#define NULL ((void *)0) +#endif +#endif + +#define SWAP32BIT(swap) ((((swap) << 24) & 0xFF000000) | \ + (((swap) << 8) & 0x00FF0000) | \ + (((swap) >> 8) & 0x0000FF00) | \ + (((swap) >> 24) & 0x000000FF)) + +#if !defined(__LITTLE_ENDIAN__) && defined(_WIN32) +#define __LITTLE_ENDIAN__ (1) +#endif + +#if defined(__LITTLE_ENDIAN__) +#define FOURCHAR(x) SWAP32BIT((UINT32)(x)) +#else +#define FOURCHAR(x) (x) +#endif + +#if !defined(TYPEDEF_STRUCT_PACK) +#define TYPEDEF_STRUCT_PACK(_x_) typedef struct __attribute__((__packed__)) _x_ +#endif + + +#include + +typedef unsigned char UCHAR; +typedef unsigned short USHORT; + +#ifndef FLOAT +typedef float FLOAT; +#endif + +#ifndef GUID +typedef struct +{ + unsigned int data1; + unsigned short data2; + unsigned short data3; + unsigned char data4[8]; +} GUID; +#endif + +#ifndef ZeroMemory +#define ZeroMemory(ptr,size) memset(ptr, 0, size) +#endif + +#ifndef strcmpi +#define strcmpi strcasecmp +#endif + +#ifndef MAX_PATH +#define MAX_PATH 256 +#endif + +// total hack for non-windows +#ifndef GUID_SysKeyboard +#define GUID_SysKeyboard (GUID){0} +#define DIK_NUMPAD7 ((INT32)0x81) +#define DIK_NUMPAD8 ((INT32)0x41) +#define DIK_NUMPAD9 ((INT32)0x21) +#define DIK_NUMPAD4 ((INT32)0x82) +#define DIK_NUMPAD5 ((INT32)0x42) +#define DIK_NUMPAD6 ((INT32)0x22) +#define DIK_NUMPAD1 ((INT32)0x84) +#define DIK_NUMPAD2 ((INT32)0x44) +#define DIK_NUMPAD3 ((INT32)0x24) +#define DIK_NUMPADPERIOD ((INT32)0x88) +#define DIK_NUMPAD0 ((INT32)0x48) +#define DIK_NUMPADENTER ((INT32)0x28) +#define DIK_SPACE ((INT32)0xA0) +#define DIK_LALT ((INT32)0x00) +#define DIK_LCONTROL ((INT32)0x60) +#define DIK_RCONTROL ((INT32)0xC0) +#define DIK_UP ((INT32)0x04) +#define DIK_W ((INT32)0x16) +#define DIK_RIGHT ((INT32)0x02) +#define DIK_S ((INT32)0x13) +#define DIK_DOWN ((INT32)0x01) +#define DIK_A ((INT32)0x19) +#define DIK_LEFT ((INT32)0x08) +#define DIK_Q ((INT32)0x1C) + +#define DIK_COMMA ((INT32)0x0) +#define DIK_N ((INT32)0x0) +#define DIK_V ((INT32)0x0) +#define DIK_X ((INT32)0x0) +#define DIK_PERIOD ((INT32)0x0) +#define DIK_M ((INT32)0x0) +#define DIK_B ((INT32)0x0) +#define DIK_C ((INT32)0x0) +#define DIK_Z ((INT32)0x0) +#define DIK_SEMICOLON ((INT32)0x0) +#define DIK_K ((INT32)0x0) +#define DIK_H ((INT32)0x0) +#define DIK_F ((INT32)0x0) +#define DIK_P ((INT32)0x0) +#define DIK_I ((INT32)0x0) +#define DIK_Y ((INT32)0x0) +#define DIK_R ((INT32)0x0) +#define DIK_GRAVE ((INT32)0x0) +#define DIK_TAB ((INT32)0x0) +#define DIK_RETURN ((INT32)0x0) +#define DIK_O ((INT32)0x0) +#define DIK_U ((INT32)0x0) +#define DIK_T ((INT32)0x0) +#define DIK_E ((INT32)0x0) +#define DIK_L ((INT32)0x0) +#define DIK_J ((INT32)0x0) +#define DIK_G ((INT32)0x0) +#define DIK_D ((INT32)0x0) +#define DIK_LSHIFT ((INT32)0x0) + +#define DIK_F2 ((INT32)0x0) + +#define DI__WTFX ((INT32)0xFA) +#define DI__WTFY ((INT32)0xFB) +#endif + +#ifdef __cplusplus +} +#endif + +#endif //TYPES_H + diff --git a/arm9/source/main.cpp b/arm9/source/main.cpp new file mode 100644 index 0000000..d6b7cb6 --- /dev/null +++ b/arm9/source/main.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include "ds_tools.h" + +#include "clickNoQuit_wav.h" +#include "clickQuit_wav.h" + +int main(int argc, char **argv) +{ + // Init sound + consoleDemoInit(); + soundEnable(); + lcdMainOnTop(); + + // Init Fat + if (!fatInitDefault()) + { + iprintf("Unable to initialize libfat!\n"); + return -1; + } + + // Init Timer + dsInitTimer(); + + // Init sound FIFO + dsInstallSoundEmuFIFO(); + + // Setup the main screen handling + dsInitScreenMain(); + + // Main loop of emulation + dsMainLoop(); + + // Free memory to be correct + dsFreeEmu(); + + return 0; +} + diff --git a/knowncarts.cfg b/knowncarts.cfg new file mode 100644 index 0000000..f40e61d --- /dev/null +++ b/knowncarts.cfg @@ -0,0 +1,699 @@ +D7C78754:Info:4-TRIS (GPL):Joseph Zbiciak:2000 +D7C78754:ROM:5000:2000:16 + +B91488E2:Info:4-TRIS (GPL):Joseph Zbiciak:2001 +B91488E2:ROM:5000:2000:16 + +A60E25FC:Info:ABPA Backgammon:Mattel:1978 +A60E25FC:ROM:5000:1000:16 + +F8B1F2B7:Info:Advanced Dungeons and Dragons:Mattel:1982 +F8B1F2B7:ROM:5000:2000:16 + +16C3B62F:Info:Advanced Dungeons and Dragons - Treasure of Tarmin:Mattel:1982 +16C3B62F:ROM:5000:2000:16 + +11C3BCFA:Info:Adventure (AD&D - Cloudy Mountain):Mattel:1982 +11C3BCFA:ROM:5000:2000:16 + +2C668249:Info:Air Strike:Mattel:1982 +2C668249:ROM:5000:1000:16 + +B45633CF:Info:All-Stars Major League Baseball:Mattel:1980 +B45633CF:ROM:5000:2000:16 +B45633CF:ROM:D000:1000:16 + +6F91FBC1:Info:Armor Battle:Mattel:1978 +6F91FBC1:ROM:5000:1000:16 + +00BE8BBA:Info:Astrosmash - Meteor:Mattel:1981 +00BE8BBA:ROM:5000:1000:16 + +FAB2992C:Info:Astrosmash:Mattel:1981 +FAB2992C:ROM:5000:1000:16 + +13FF363C:Info:Atlantis:Imagic:1981 +13FF363C:ROM:4800:2000:16 + +B35C1101:Info:Auto Racing:Mattel:1979 +B35C1101:ROM:5000:1000:16 + +8AD19AB3:Info:B-17 Bomber:Mattel:1981 +8AD19AB3:ROM:5000:2000:16 +8AD19AB3:ROM:D000:1000:16 +8AD19AB3:Peripheral:Intellivoice:Optional + +DAB36628:Info:Baseball:Mattel:1978 +DAB36628:ROM:5000:1000:16 + +EAF650CC:Info:BeamRider:Activision:1983 +EAF650CC:ROM:5000:2000:16 + +C047D487:Info:Beauty and the Beast:Imagic:1982 +C047D487:ROM:4800:2000:16 + +B03F739B:Info:Blockade Runner:Interphase:1983 +B03F739B:ROM:5000:2000:16 + +4728C3BD:Info:Blow Out:Mattel:1983 +4728C3BD:ROM:5000:1000:16 + +515E1D7E:Info:Body Slam Super Pro Wrestling:Mattel:1988 +515E1D7E:ROM:5000:2000:16 +515E1D7E:ROM:9000:2000:16 + +32697B72:Info:Bomb Squad:Mattel:1982 +32697B72:ROM:5000:2000:16 +32697B72:ROM:D000:1000:16 +32697B72:Peripheral:Intellivoice:Optional + +18E08520:Info:Bouncing Pixels (GPL):JRMZ Electronics:1999 +18E08520:ROM:5000:0202:16 + +AB87C16F:Info:Boxing:Mattel:1980 +AB87C16F:ROM:5000:1000:16 + +9F85015B:Info:Brickout!:Mattel:1981 +9F85015B:ROM:5000:1000:16 + +999CCEED:Info:Bump 'N' Jump:Mattel:1982-83 +999CCEED:ROM:5000:2000:16 +999CCEED:ROM:D000:1000:16 +999CCEED:ROM:F000:1000:16 + +43806375:Info:BurgerTime!:Mattel:1982 +43806375:ROM:5000:2000:16 + +C92BAAE8:Info:BurgerTime! - New Levels Hack:David Harley:2002 +C92BAAE8:ROM:5000:2000:16 + +FA492BBD:Info:Buzz Bombers:Mattel:1982 +FA492BBD:ROM:5000:2000:16 + +2A1E0C1C:Info:Buzz Bombers:Mattel:1982 +2A1E0C1C:ROM:5000:2000:16 + +43870908:Info:Carnival:Coleco:1982 +43870908:ROM:5000:1000:16 + +D5363B8C:Info:Centipede:Atarisoft:1983 +D5363B8C:ROM:6000:2000:16 + +4CC46A04:Info:Championship Tennis:Nice Ideas:1985 +4CC46A04:ROM:5000:2000:16 +4CC46A04:ROM:D000:2000:16 + +36E1D858:Info:Checkers:Mattel:1979 +36E1D858:ROM:5000:1000:16 + +0BF464C6:Info:Chip Shot Super Pro Golf:Mattel:1987 +0BF464C6:ROM:5000:2000:16 +0BF464C6:ROM:9000:2000:16 + +47FDD8A8:Info:Choplifter:Mattel:1983 +47FDD8A8:ROM:5000:2000:16 +47FDD8A8:ROM:D000:2000:16 + +3289C8BA:Info:Commando:INTV:1987 +3289C8BA:ROM:5000:2000:16 +3289C8BA:ROM:9000:2000:16 + +4B23A757:Info:Congo Bongo:Sega:1983 +4B23A757:ROM:5000:3000:16 + +E1EE408F:Info:Crazy Clones:Mattel:1981 +E1EE408F:ROM:5000:1000:16 + +CDC14ED8:Info:Deadly Dogs!:Unknown:1987 +CDC14ED8:ROM:5000:1000:16 + +6802B191:Info:Deep Pockets - Super Pro Pool and Billiards:Realtime:1990 +6802B191:ROM:5000:2000:16 +6802B191:ROM:9000:2000:16 + +D8F99AA2:Info:Defender:Atarisoft:1983 +D8F99AA2:ROM:5000:3000:16 + +5E6A8CD8:Info:Demon Attack:Imagic:1982 +5E6A8CD8:ROM:4800:2000:16 + +159AF7F7:Info:Dig Dug:INTV:1987 +159AF7F7:ROM:5000:3000:16 +159AF7F7:ROM:9000:1000:16 + +13EE56F1:Info:Diner:INTV:1987 +13EE56F1:ROM:5000:2000:16 +13EE56F1:ROM:9000:2000:16 + +C30F61C0:Info:Donkey Kong:Coleco:1982 +C30F61C0:ROM:5000:1000:16 + +6DF61A9F:Info:Donkey Kong Jr:Coleco:1982 +6DF61A9F:ROM:5000:2000:16 + +84BEDCC1:Info:Dracula:Imagic:1982 +84BEDCC1:ROM:5000:2000:16 + +AF8718A1:Info:Dragonfire:Imagic:1982 +AF8718A1:ROM:5000:1000:16 + +3B99B889:Info:Dreadnaught Factor, The:Activision:1983 +3B99B889:ROM:5000:2000:16 + +BF4D0E9B:Info:Dreadnaught Factor, The (Prototype):Activision:1983 +BF4D0E9B:ROM:5000:2000:16 + +20ACE89D:Info:Easter Eggs:Mattel:1981 +20ACE89D:ROM:5000:1000:16 + +54A3FC11:Info:Electric Company - Math Fun:CTW:1978 +54A3FC11:ROM:5000:1000:16 + +C9EAACAB:Info:Electric Company - Word Fun:CTW:1980 +C9EAACAB:ROM:5000:1000:16 + +4221EDE7:Info:Fathom:Imagic:1983 +4221EDE7:ROM:5000:2000:16 + +37222762:Info:Frog Bog:Mattel:1982 +37222762:ROM:5000:1000:16 + +D27495E9:Info:Frogger:Parker Bros:1983 +D27495E9:ROM:5000:1000:16 + +DBCA82C5:Info:Go For The Gold:Mattel:1981 +DBCA82C5:ROM:5000:2000:16 + +291AC826:Info:Grid Shock:Mattel:1982 +291AC826:ROM:5000:1000:16 + +E573863A:Info:Groovy! (GPL):JRMZ Electronics:1999 +E573863A:ROM:5000:1E48:16 + +4B8C5932:Info:Happy Trails:Activision:1983 +4B8C5932:ROM:5000:1000:16 + +120B53A9:Info:Happy Trails (Overdump):Activision:1983 +120B53A9:ROM:5000:1000:16 + +B6A3D4DE:Info:Hard Hat:Mattel:1979 +B6A3D4DE:ROM:5000:2000:16 + +B5C7F25D:Info:Horse Racing:Mattel:1980 +B5C7F25D:ROM:5000:1000:16 + +FF83FF80:Info:Hover Force:Mattel:1986 +FF83FF80:ROM:5000:2000:16 +FF83FF80:ROM:9000:3000:16 +FF83FF80:ROM:D000:1000:16 + +A3147630:Info:Hypnotic Lights:Mattel:1981 +A3147630:ROM:5000:1000:16 + +4F3E3F69:Info:Ice Trek:Imagic:1983 +4F3E3F69:ROM:5000:2000:16 + +02919024:Info:Intellivision - Intelligent Television Demo:Mattel:1978 +02919024:ROM:5000:2000:16 + +C83EEA4C:Info:Intellivision Test Cartridge and Baseball:Mattel:1978 +C83EEA4C:ROM:5000:1000:16 +C83EEA4C:ROM:7000:1000:16 + +985A78ED:Info:IntyOS 0.2 Alpha:Arnauld Chevallier:2003 +985A78ED:ROM:5000:1000:16 +985A78ED:RAM:D000:1000:16 +985A78ED:RAM:F000:1000:16 + +EE5F1BE2:Info:Jetsons, The - Ways With Words:Mattel:1983 +EE5F1BE2:ROM:5000:2000:16 +EE5F1BE2:ROM:D000:1000:16 +EE5F1BE2:Peripheral:ECS:Required + +4422868E:Info:King of the Mountain:Mattel:1982 +4422868E:ROM:5000:2000:16 +4422868E:ROM:D000:2000:16 + +8C9819A2:Info:Kool-Aid Man:Mattel:1983 +8C9819A2:ROM:5000:2000:16 + +A6840736:Info:Lady Bug:Coleco:1983 +A6840736:ROM:5000:2000:16 + +3825C25B:Info:Land Battle:Mattel:1982 +3825C25B:ROM:5000:2000:16 +3825C25B:RAM:D000:400:8 + +604611C0:Info:Las Vegas Blackjack and Poker:Mattel:1979 +604611C0:ROM:5000:1000:16 + +48D74D3C:Info:Las Vegas Roulette:Mattel:1979 +48D74D3C:ROM:5000:1000:16 + +19360442:Info:League of Light (Prototype Alt1):Activision:1983 +19360442:ROM:5000:2000:16 + +75EE64F6:Info:League of Light (Prototype Alt2):Activision:1983 +75EE64F6:ROM:5000:2000:16 + +B4287B95:Info:League of Light (Prototype):Activision:1983 +B4287B95:ROM:5000:2000:16 + +2C5FD5FA:Info:Learning Fun I - Math Master Factor Fun:INTV:1987 +2C5FD5FA:ROM:5000:2000:16 + +632F6ADF:Info:Learning Fun II - Word Wizard Memory Fun:INTV:1987 +632F6ADF:ROM:5000:2000:16 + +E00D1399:Info:Lock 'N' Chase:Mattel:1982 +E00D1399:ROM:5000:2000:16 + +6B6E80EE:Info:Loco-Motion:Mattel:1982 +6B6E80EE:ROM:5000:2000:16 + +F3B0C759:Info:Magic Carousel:Mattel:1982 +F3B0C759:ROM:5000:2000:16 +F3B0C759:ROM:D000:2000:16 + +573B9B6D:Info:Masters of the Universe - The Power of He-Man!:Mattel:1983 +573B9B6D:ROM:5000:2000:16 +573B9B6D:ROM:D000:1000:16 +573B9B6D:ROM:F000:1000:16 + +7D0F8162:Info:Maze Demo #1 (GPL):JRMZ Electronics:2000 +7D0F8162:ROM:5000:016E:16 + +2138DAD4:Info:Maze Demo #2 (GPL):JRMZ Electronics:2000 +2138DAD4:ROM:5000:0175:16 + +FF68AA22:Info:Melody Blaster:Mattel:1983 +FF68AA22:ROM:5000:2000:16 +FF68AA22:ROM:D000:1000:16 +FF68AA22:Peripheral:ECS:Required + +E00E23E7:Info:Mickey's Hello World:Mickey:2000 +E00E23E7:ROM:5000:006F:16 + +E806AD91:Info:Microsurgeon:Imagic:1982 +E806AD91:ROM:4800:2000:16 + +94096229:Info:Minehunter:Ryan Kinnen:2004 +94096229:ROM:5000:2000:16 + +9D57498F:Info:Mind Strike!:Mattel:1982 +9D57498F:ROM:5000:2000:16 +9D57498F:ROM:D000:1000:16 +9D57498F:Peripheral:ECS:Required + +6746607B:Info:Minotaur V1.1:Mattel:1981 +6746607B:ROM:5000:2000:16 + +5A4CE519:Info:Minotaur V2:Mattel:1981 +5A4CE519:ROM:5000:2000:16 + +BD731E3C:Info:Minotaur:Mattel:1981 +BD731E3C:ROM:5000:2000:16 + +2F9C93FC:Info:Minotaur (Treasure of Tarmin hack):Mattel:1981 +2F9C93FC:ROM:5000:2000:16 + +11FB9974:Info:Mission X:Mattel:1982 +11FB9974:ROM:5000:2000:16 + +5F6E1AF6:Info:Motocross:Mattel:1982 +5F6E1AF6:ROM:5000:2000:16 + +6B5EA9C4:Info:Mountain Madness Super Pro Skiing:INTV:1987 +6B5EA9C4:ROM:5000:2000:16 + +598662F2:Info:Mouse Trap:Coleco:1982 +598662F2:ROM:5000:1000:16 + +0B50A367:Info:Mr. Basic Meets Bits 'N Bytes (Bad Dump):Mattel:1983 +0B50A367:ROM:5000:2000:16 +0B50A367:ROM:D000:1000:16 +0B50A367:Peripheral:ECS:Required + +BEF0B0C7:Info:Mr. Basic Meets Bits 'N Bytes:Mattel:1983 +BEF0B0C7:ROM:5000:2000:16 +BEF0B0C7:ROM:D000:1000:16 +BEF0B0C7:Peripheral:ECS:Required + +DBAB54CA:Info:NASL Soccer:Mattel:1979 +DBAB54CA:ROM:5000:1000:16 + +81E7FB8C:Info:NBA Basketball:Mattel:1978 +81E7FB8C:ROM:5000:1000:16 + +4B91CF16:Info:NFL Football:Mattel:1978 +4B91CF16:ROM:5000:1000:16 + +76564A13:Info:NHL Hockey:Mattel:1979 +76564A13:ROM:5000:1000:16 + +7334CD44:Info:Night Stalker:Mattel:1982 +7334CD44:ROM:5000:1000:16 + +5EE2CC2A:Info:Nova Blast:Imagic:1983 +5EE2CC2A:ROM:5000:2000:16 + +E5D1A8D2:Info:Number Jumble:Mattel:1983 +E5D1A8D2:ROM:5000:2000:16 +E5D1A8D2:ROM:D000:1000:16 +E5D1A8D2:ROM:F000:1000:16 + +A21C31C3:Info:Pac-Man:Atarisoft:1983 +A21C31C3:ROM:5000:3000:16 + +6E4E8EB4:Info:Pac-Man:INTV:1983 +6E4E8EB4:ROM:5000:3000:16 + +169E3584:Info:PBA Bowling:Mattel:1980 +169E3584:ROM:5000:1000:16 + +FF87FAEC:Info:PGA Golf:Mattel:1979 +FF87FAEC:ROM:5000:1000:16 + +D7C5849C:Info:Pinball:Mattel:1981 +D7C5849C:ROM:5000:2000:16 +D7C5849C:ROM:D000:1000:16 + +9C75EFCC:Info:Pitfall!:Activision:1982 +9C75EFCC:ROM:5000:1000:16 + +BB939881:Info:Pole Position:INTV:1986 +BB939881:ROM:5000:2000:16 +BB939881:ROM:9000:2000:16 + +A982E8D5:Info:Pong:Unknown:1999 +A982E8D5:ROM:5000:0800:16 + +C51464E0:Info:Popeye:Parker Bros:1983 +C51464E0:ROM:5000:2000:16 + +D8C9856A:Info:Q-bert:Parker Bros:1983 +D8C9856A:ROM:5000:2000:16 + +C7BB1B0E:Info:Reversi:Mattel:1984 +C7BB1B0E:ROM:5000:1000:16 + +8910C37A:Info:River Raid:Activision:1982-83 +8910C37A:ROM:5000:2000:16 + +95466AD3:Info:River Raid V1 (Prototype):Activision:1982-83 +95466AD3:ROM:5000:2000:16 + +1682D0B4:Info:Robot Rubble V1 (Prototype) (Overdump):Activision:1983 +1682D0B4:ROM:5000:2000:16 + +7473916D:Info:Robot Rubble V1 (Prototype):Activision:1983 +7473916D:ROM:5000:2000:16 + +A5E28783:Info:Robot Rubble V2 (Prototype):Activision:1983 +A5E28783:ROM:5000:2000:16 + +243B0812:Info:Robot Rubble V3 (Prototype):Activision:1983 +243B0812:ROM:5000:2000:16 + +DCF4B15D:Info:Royal Dealer:Mattel:1981 +DCF4B15D:ROM:5000:2000:16 + +47AA7977:Info:Safecracker:Imagic:1983 +47AA7977:ROM:5000:2000:16 + +6E0882E7:Info:SameGame and Robots:IntelligentVision:2005 +6E0882E7:ROM:5000:2000:16 +6E0882E7:ROM:D000:1000:16 +6E0882E7:ROM:F000:1000:16 + +12BA58D1:Info:SameGame and Robots (Original):Michael J Hayes:2004 +12BA58D1:ROM:5000:2000:16 +12BA58D1:ROM:D000:1000:16 +12BA58D1:ROM:F000:1000:16 + +E221808C:Info:Santa's Helper:Mattel:1983 +E221808C:ROM:5000:1000:16 + +E9E3F60D:Info:Scooby Doo's Maze Chase:Mattel/Hanna-Barbera:1983 +E9E3F60D:ROM:5000:2000:16 +E9E3F60D:Peripheral:ECS:Required + +99AE29A9:Info:Sea Battle:Mattel:1980 +99AE29A9:ROM:5000:1000:16 + +E0F0D3DA:Info:Sewer Sam:Interphase:1983 +E0F0D3DA:ROM:5000:2000:16 + +A610406E:Info:Shape Escape:John Doherty:2005 +A610406E:ROM:5000:2000:16 + +2A4C761D:Info:Shark! Shark!:Mattel:1982 +2A4C761D:ROM:5000:2000:16 + +FF7CB79E:Info:Sharp Shot:Mattel:1982 +FF7CB79E:ROM:5000:1000:16 + +800B572F:Info:Slam Dunk Super Pro Basketball:INTV:1987 +800B572F:ROM:5000:2000:16 +800B572F:ROM:9000:2000:16 + +BA68FF28:Info:Slap Shot Super Pro Hockey:INTV:1987 +BA68FF28:ROM:5000:2000:16 + +8F959A6E:Info:Snafu:Mattel:1981 +8F959A6E:ROM:5000:1000:16 + +72C2A343:Info:Song Player - Back in the USSR (GPL):Joseph Zbiciak:1999 +72C2A343:ROM:5000:2000:16 +72C2A343:ROM:9000:1241:16 + +5E3130E1:Info:Song Player - Copacabana (GPL):Joseph Zbiciak:1999 +5E3130E1:ROM:5000:2000:16 +5E3130E1:ROM:9000:088F:16 +5E3130E1:Peripheral:ECS:Required + +C91B1D79:Info:Song Player - Creep (GPL):Joseph Zbiciak:1999 +C91B1D79:ROM:5000:1081:16 +C91B1D79:Peripheral:ECS:Required + +82D2E346:Info:Song Player - Nut March (GPL):Joseph Zbiciak:1999 +82D2E346:ROM:5000:1B12:16 +82D2E346:Peripheral:ECS:Required + +FCF06A8B:Info:Song Player - Nut Reed (GPL):Joseph Zbiciak:1999 +FCF06A8B:ROM:5000:145E:16 +FCF06A8B:Peripheral:ECS:Required + +5914220A:Info:Song Player - Nut Trep (GPL):Joseph Zbiciak:1999 +5914220A:ROM:5000:0DC8:16 +5914220A:Peripheral:ECS:Required + +71F5BE4E:Info:Song Player - Nut Waltz (GPL):Joseph Zbiciak:1999 +71F5BE4E:ROM:5000:2000:16 +71F5BE4E:ROM:9000:157C:16 +71F5BE4E:Peripheral:ECS:Required + +437C0E2D:Info:Song Player - Secret Agent Man (GPL):Joseph Zbiciak:1999 +437C0E2D:ROM:5000:2000:16 +437C0E2D:ROM:9000:15A4:16 +437C0E2D:Peripheral:ECS:Required + +DA5D77EA:Info:Song Player - Take on Me 3 (GPL):Joseph Zbiciak:1999 +DA5D77EA:ROM:5000:12FD:16 +DA5D77EA:Peripheral:ECS:Required + +10D721CA:Info:Song Player - Take on Me 6 (GPL):Joseph Zbiciak:1999 +10D721CA:ROM:5000:1500:16 +10D721CA:Peripheral:ECS:Required + +CC6F4F90:Info:Song Player - Too Sexy (GPL):Joseph Zbiciak:1999 +CC6F4F90:ROM:5000:1AA7:16 +CC6F4F90:Peripheral:ECS:Required + +E8B8EBA5:Info:Space Armada:Mattel:1981 +E8B8EBA5:ROM:5000:1000:16 + +F95504E0:Info:Space Battle:Mattel:1979 +F95504E0:ROM:5000:1000:16 + +F8EF3E5A:Info:Space Cadet:Mattel:1982 +F8EF3E5A:ROM:5000:2000:16 + +39D3B895:Info:Space Hawk:Mattel:1981 +39D3B895:ROM:5000:1000:16 + +E98B9163:Info:Space Shuttle:Mattel:1983 +E98B9163:ROM:5000:2000:16 +E98B9163:ROM:D000:3000:16 +E98B9163:Peripheral:Intellivoice:Optional + +3784DC52:Info:Space Spartans:Mattel:1981 +3784DC52:ROM:5000:2000:16 +3784DC52:Peripheral:Intellivoice:Optional + +A95021FC:Info:Spiker! Super Pro Volleyball:INTV:1988 +A95021FC:ROM:5000:2000:16 +A95021FC:ROM:9000:2000:16 + +B745C1CA:Info:Stadium Mud Buggies:INTV:1988 +B745C1CA:ROM:5000:2000:16 +B745C1CA:ROM:9000:2000:16 + +2DEACD15:Info:Stampede:Activision:1982 +2DEACD15:ROM:5000:1000:16 + +72E11FCA:Info:Star Strike:Mattel:1981 +72E11FCA:ROM:5000:1000:16 + +D5B0135A:Info:Star Wars - The Empire Strikes Back:Parker Bros:1983 +D5B0135A:ROM:5000:1000:16 + +A03EDF73:Info:Stack Em:Arnauld Chevallier:2004 +A03EDF73:ROM:4800:2000:16 + +66D396C0:Info:Stonix:Arnauld Chevallier:2004 +66D396C0:ROM:5000:2000:16 +66D396C0:ROM:D000:1000:16 +66D396C0:ROM:F000:1000:16 + +B119027D:Info:Stonix Beta 1.2:Arnauld Chevallier:2003 +B119027D:ROM:5000:2000:16 + +4830F720:Info:Street:Mattel:1981 +4830F720:ROM:5000:1000:16 + +3D9949EA:Info:Sub Hunt:Mattel:1981 +3D9949EA:ROM:5000:2000:16 + +8F7D3069:Info:Super Cobra:Konami:1983 +8F7D3069:ROM:5000:2000:16 + +BAB638F2:Info:Super Masters!:Mattel:1982 +BAB638F2:ROM:5000:2000:16 + +16BFB8EB:Info:Super Pro Decathlon:INTV:1978 +16BFB8EB:ROM:5000:2000:16 +16BFB8EB:ROM:9000:2000:16 + +32076E9D:Info:Super Pro Football:INTV:1986 +32076E9D:ROM:5000:2000:16 +32076E9D:ROM:9000:2000:16 + +51B82EB7:Info:Super Soccer:Mattel:1983 +51B82EB7:ROM:5000:2000:16 +51B82EB7:ROM:D000:1000:16 +51B82EB7:ROM:F000:1000:16 + +15E88FCE:Info:Swords and Serpents:Imagic:1982 +15E88FCE:ROM:5000:2000:16 + +1F584A69:Info:Takeover:Mattel:1982 +1F584A69:ROM:5000:1000:16 + +03E9E62E:Info:Tennis:Mattel:1980 +03E9E62E:ROM:5000:1000:16 + +D43FD410:Info:Tetris (GPL):Joseph Zbiciak:2000 +D43FD410:ROM:5000:2000:16 + +F3DF94E0:Info:Thin Ice:Mattel:1983 +F3DF94E0:ROM:5000:2000:16 +F3DF94E0:ROM:D000:1000:16 +F3DF94E0:ROM:F000:1000:16 + +D6495910:Info:Thin Ice (Prototype):Mattel:1983 +D6495910:ROM:5000:2000:16 + +C1F1CA74:Info:Thunder Castle:Mattel:1982 +C1F1CA74:ROM:5000:2000:16 +C1F1CA74:ROM:D000:1000:16 +C1F1CA74:ROM:F000:1000:16 + +D1D352A0:Info:Tower of Doom:Mattel:1986 +D1D352A0:ROM:5000:2000:16 +D1D352A0:ROM:9000:2000:16 +D1D352A0:ROM:D000:1000:16 +D1D352A0:ROM:F000:1000:16 + +1AC989E2:Info:Triple Action:Mattel:1981 +1AC989E2:ROM:5000:1000:16 + +095638C0:Info:Triple Challenge:INTV:1986 +095638C0:ROM:5000:2000:16 +095638C0:ROM:9000:2000:16 +095638C0:ROM:C000:0800:16 +095638C0:ROM:D000:1000:16 + +7A558CF5:Info:Tron Maze-A-Tron:Mattel:1981 +7A558CF5:ROM:5000:2000:16 + +CA447BBD:Info:Tron Deadly Discs:Mattel:1981 +CA447BBD:ROM:5000:1000:16 + +07FB9435:Info:Tron Solar Sailer:Mattel:1982 +07FB9435:ROM:5000:2000:16 +07FB9435:ROM:D000:1000:16 +07FB9435:Peripheral:Intellivoice:Optional + +6F23A741:Info:Tropical Trouble:Imagic:1982 +6F23A741:ROM:5000:2000:16 + +734F3260:Info:Truckin':Imagic:1983 +734F3260:ROM:5000:2000:16 + +275F3512:Info:Turbo:Coleco:1983 +275F3512:ROM:5000:2000:16 + +6FA698B3:Info:Tutankham:Parker Bros:1983 +6FA698B3:ROM:5000:2000:16 + +F093E801:Info:U.S. Ski Team Skiing:Mattel:1980 +F093E801:ROM:5000:1000:16 + +752FD927:Info:USCF Chess:Mattel:1981 +752FD927:ROM:5000:2000:16 +752FD927:RAM:D000:400:8 + +F9E0789E:Info:Utopia:Mattel:1981 +F9E0789E:ROM:5000:1000:16 + +A4A20354:Info:Vectron:Mattel:1982 +A4A20354:ROM:5000:2000:16 +A4A20354:ROM:D000:1000:16 + +6EFA67B2:Info:Venture:Coleco:1982 +6EFA67B2:ROM:5000:2000:16 + +F1ED7D27:Info:White Water!:Imagic:1983 +F1ED7D27:ROM:5000:2000:16 + +15D9D27A:Info:World Cup Football:Nice Ideas:1985 +15D9D27A:ROM:5000:2000:16 +15D9D27A:ROM:D000:1000:16 +15D9D27A:ROM:F000:1000:16 + +C2063C08:Info:World Series Major League Baseball:Mattel:1983 +C2063C08:ROM:5000:2000:16 +C2063C08:ROM:D000:1000:16 +C2063C08:ROM:E000:1000:16:EFFF,FFF0=EA50:000F=0000 +C2063C08:ROM:F000:1000:16:FFFF,FFF0=FA50:000F=0000 +C2063C08:ROM:F000:1000:16:FFFF,FFF0=FA50:000F=0001 +C2063C08:Peripheral:Intellivoice:Optional +C2063C08:Peripheral:ECS:Required + +A12C27E1:Info:World Series Major League Baseball (Bad Dump):Mattel:1983 +A12C27E1:ROM:5000:2000:16 +A12C27E1:ROM:D000:1000:16 +A12C27E1:ROM:E000:1000:16:EFFF,FFF0=EA50:000F=0000 +A12C27E1:ROM:F000:1000:16:FFFF,FFF0=FA50:000F=0000 +A12C27E1:Peripheral:Intellivoice:Optional +A12C27E1:Peripheral:ECS:Required + +24B667B9:Info:Worm Whomper:Activision:1983 +24B667B9:ROM:5000:1000:16 + +15C65DC5:Info:Zaxxon:Coleco:1982 +15C65DC5:ROM:5000:2000:16 + +D89AEC27:Info:Zombie Marbles:John Doherty:2004 +D89AEC27:ROM:5000:2000:16 diff --git a/logo.bmp b/logo.bmp new file mode 100644 index 0000000000000000000000000000000000000000..25efebd1bbc6366fdf990f60960babed7caf65a7 GIT binary patch literal 630 zcma)2F%H5o3=D{kkq1!zT2Op+5&w`RVy5rLNTO!aW7x7b zMq*5)F*4SvG87hbQFXX?%{=%O)ZMO82g*= literal 0 HcmV?d00001