diff --git a/.gitignore b/.gitignore index cd2891a4..ccc94c99 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ lsnes /core /bsnes /gambatte +/meteor src/fonts/font.cpp src/core/version.cpp diff --git a/manual.lyx b/manual.lyx index a339f324..a7995378 100644 --- a/manual.lyx +++ b/manual.lyx @@ -99,7 +99,21 @@ SVN r320, r358 or r364 \end_layout \begin_layout Enumerate -Patched with included patches +Patched with included patch. +\end_layout + +\end_deeper +\begin_layout Enumerate +meteor (for meteor core) +\end_layout + +\begin_deeper +\begin_layout Enumerate +1.4.0 +\end_layout + +\begin_layout Enumerate +Patched with included patch. \end_layout \end_deeper diff --git a/manual.txt b/manual.txt index 45370be3..bf4fd20b 100644 --- a/manual.txt +++ b/manual.txt @@ -16,33 +16,39 @@ lsnes is SNES rerecording emulator based on bsnes core. (a) SVN r320, r358 or r364 - (b) Patched with included patches + (b) Patched with included patch. -3. Zlib +3. meteor (for meteor core) -4. boost_iostreams + (a) 1.4.0 -5. boost_filesystem + (b) Patched with included patch. -6. boost_thread (if native std::thread is not available) +4. Zlib -7. libsdl (SDL only) +5. boost_iostreams -8. sdlmain (SDL only, part of SDL) +6. boost_filesystem -9. boost_conversion (this is header-only library) +7. boost_thread (if native std::thread is not available) -10. libswscale (wxwidgets graphics only) +8. libsdl (SDL only) -11. Portaudio (portaudio sound only) +9. sdlmain (SDL only, part of SDL) -12. libao (libao sound only) +10. boost_conversion (this is header-only library) -13. Lua version 5.1.X or 5.2.X +11. libswscale (wxwidgets graphics only) -14. G++ 4.6 or 4.7 +12. Portaudio (portaudio sound only) -15. libopus (optional, for commentary track tool) +13. libao (libao sound only) + +14. Lua version 5.1.X or 5.2.X + +15. G++ 4.6 or 4.7 + +16. libopus (optional, for commentary track tool) 3 Building diff --git a/meteor-patches/1.4.0/0001-Add-memory-watch-features.patch b/meteor-patches/1.4.0/0001-Add-memory-watch-features.patch new file mode 100644 index 00000000..e214caa5 --- /dev/null +++ b/meteor-patches/1.4.0/0001-Add-memory-watch-features.patch @@ -0,0 +1,489 @@ +From c3059a59fa95cd0e165dbcf7df4b9dc11d5b8d29 Mon Sep 17 00:00:00 2001 +From: Ilari Liusvaara +Date: Sun, 17 Feb 2013 11:18:50 +0200 +Subject: [PATCH] Add memory watch features + +--- + Makefile | 60 ++++++++++++++++++ + ameteor/include/ameteor/cartmem.hpp | 2 + + ameteor/include/ameteor/graphics/bglayer.hpp | 3 + + ameteor/include/ameteor/io.hpp | 3 + + ameteor/include/ameteor/memory.hpp | 2 + + ameteor/source/clock.cpp | 3 +- + ameteor/source/graphics/bglayer.cpp | 85 ++++++++++++++------------ + ameteor/source/graphics/screen.cpp | 22 +++++++ + ameteor/source/io.cpp | 15 ++++- + ameteor/source/memory.cpp | 33 +++++++++-- + 10 files changed, 180 insertions(+), 48 deletions(-) + create mode 100644 Makefile + +diff --git a/Makefile b/Makefile +new file mode 100644 +index 0000000..463a2f7 +--- /dev/null ++++ b/Makefile +@@ -0,0 +1,60 @@ ++TARGET=libmeteor.$(ARCHIVE_SUFFIX) ++CXXFLAGS += -Wall -Wno-parentheses -I. -Iameteor/include -D__LIBRETRO__ -O3 -g ++ ++SRCDIR := ameteor/source ++ ++SOURCES := \ ++ $(SRCDIR)/audio/dsound.cpp \ ++ $(SRCDIR)/audio/sound1.cpp \ ++ $(SRCDIR)/audio/sound2.cpp \ ++ $(SRCDIR)/audio/sound4.cpp \ ++ $(SRCDIR)/audio/speaker.cpp \ ++ $(SRCDIR)/disassembler/argimmediate.cpp \ ++ $(SRCDIR)/disassembler/argmulregisters.cpp \ ++ $(SRCDIR)/disassembler/argpsr.cpp \ ++ $(SRCDIR)/disassembler/argregister.cpp \ ++ $(SRCDIR)/disassembler/argrelative.cpp \ ++ $(SRCDIR)/disassembler/argshift.cpp \ ++ $(SRCDIR)/disassembler/arguimmediate.cpp \ ++ $(SRCDIR)/disassembler/arguments.cpp \ ++ $(SRCDIR)/disassembler/instruction.cpp \ ++ $(SRCDIR)/graphics/bglayer.cpp \ ++ $(SRCDIR)/graphics/object.cpp \ ++ $(SRCDIR)/graphics/objects.cpp \ ++ $(SRCDIR)/graphics/renderer.cpp \ ++ $(SRCDIR)/graphics/screen.cpp \ ++ $(SRCDIR)/ameteor.cpp \ ++ $(SRCDIR)/bios.cpp \ ++ $(SRCDIR)/clock.cpp \ ++ $(SRCDIR)/cpu.cpp \ ++ $(SRCDIR)/debug.cpp \ ++ $(SRCDIR)/dma.cpp \ ++ $(SRCDIR)/eeprom.cpp \ ++ $(SRCDIR)/flash.cpp \ ++ $(SRCDIR)/cartmem.cpp \ ++ $(SRCDIR)/interpreter.cpp \ ++ $(SRCDIR)/interpreter_arm.cpp \ ++ $(SRCDIR)/interpreter_thumb.cpp \ ++ $(SRCDIR)/io.cpp \ ++ $(SRCDIR)/keypad.cpp \ ++ $(SRCDIR)/lcd.cpp \ ++ $(SRCDIR)/memory.cpp \ ++ $(SRCDIR)/sound.cpp \ ++ $(SRCDIR)/sram.cpp \ ++ $(SRCDIR)/timer.cpp ++ ++OBJ := $(SOURCES:.cpp=.$(OBJECT_SUFFIX)) ++ ++all: $(TARGET) ++ ++$(TARGET): $(OBJ) ++ $(REALAR) crvs $@ $^ ++ ++%.$(OBJECT_SUFFIX): %.cpp ++ $(CXX) -o $@ -c $< $(CXXFLAGS) ++ ++clean: ++ rm -f $(TARGET) ++ rm -f $(OBJ) ++ ++.PHONY: clean +diff --git a/ameteor/include/ameteor/cartmem.hpp b/ameteor/include/ameteor/cartmem.hpp +index e7ee436..a18e6d6 100644 +--- a/ameteor/include/ameteor/cartmem.hpp ++++ b/ameteor/include/ameteor/cartmem.hpp +@@ -44,6 +44,8 @@ namespace AMeteor + virtual bool SaveState (std::ostream& stream); + virtual bool LoadState (std::istream& stream); + ++ uint8_t* GetRawData() { return m_data; } ++ uint32_t GetRawSize() { return m_size; } + protected: + uint8_t* m_data; + uint32_t m_size; +diff --git a/ameteor/include/ameteor/graphics/bglayer.hpp b/ameteor/include/ameteor/graphics/bglayer.hpp +index 1409929..48d19ff 100644 +--- a/ameteor/include/ameteor/graphics/bglayer.hpp ++++ b/ameteor/include/ameteor/graphics/bglayer.hpp +@@ -46,6 +46,9 @@ namespace AMeteor + void DrawLine4 (uint8_t line, uint16_t* ptr, + int32_t curX, int32_t curY, + int16_t dx, int16_t dmx, int16_t dy, int16_t dmy, bool frame1); ++ void DrawLine5 (uint16_t* ptr, ++ int32_t refX, int32_t refY, ++ int16_t dx, int16_t dy, bool frame1); + void FillList (); + + void UpdateCnt (uint16_t cnt); +diff --git a/ameteor/include/ameteor/io.hpp b/ameteor/include/ameteor/io.hpp +index 5f553c0..108e41f 100644 +--- a/ameteor/include/ameteor/io.hpp ++++ b/ameteor/include/ameteor/io.hpp +@@ -208,8 +208,11 @@ namespace AMeteor + bool SaveState (std::ostream& stream); + bool LoadState (std::istream& stream); + ++ bool GetPolled() { return m_polled; } ++ void SetPolled(bool f) { m_polled = f; } + private : + uint8_t* m_iomem; ++ bool m_polled; + }; + } + +diff --git a/ameteor/include/ameteor/memory.hpp b/ameteor/include/ameteor/memory.hpp +index a45652d..e3d1aa4 100644 +--- a/ameteor/include/ameteor/memory.hpp ++++ b/ameteor/include/ameteor/memory.hpp +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + namespace AMeteor + { +@@ -119,6 +120,7 @@ namespace AMeteor + + void TimeEvent (); + ++ std::map > GetMemories(); + private : + // times for a 8 or 16 bits access + uint8_t m_memtime[0xF]; +diff --git a/ameteor/source/clock.cpp b/ameteor/source/clock.cpp +index 7f21980..460db6a 100644 +--- a/ameteor/source/clock.cpp ++++ b/ameteor/source/clock.cpp +@@ -35,12 +35,11 @@ namespace AMeteor + { + unsigned short tocommit; + +- m_count += m_cycles; +- + // this loop is here because a timer can trigger a dma which will take a + // long time, during this time the lcd must draw and the timers continue + while (m_cycles >= m_first) + { ++ m_count += m_cycles; + tocommit = m_cycles; + m_cycles = 0; + +diff --git a/ameteor/source/graphics/bglayer.cpp b/ameteor/source/graphics/bglayer.cpp +index 72eea70..2c9eea4 100644 +--- a/ameteor/source/graphics/bglayer.cpp ++++ b/ameteor/source/graphics/bglayer.cpp +@@ -133,11 +133,11 @@ namespace AMeteor + else + *ptr = 0x0; + +- if (flipH) +- --tpChar; +- else +- ++tpChar; + } ++ if (flipH) ++ --tpChar; ++ else ++ ++tpChar; + + ++ptr; + ++tileX; +@@ -186,27 +186,27 @@ namespace AMeteor + // we draw until the end of the tile or the line + while (tileX < 8) + { ++ if (flipH) ++ if (tileX % 2) ++ { ++ colorInd = *tpChar & 0xF; ++ --tpChar; ++ } ++ else ++ colorInd = *tpChar >> 4; ++ else ++ if (tileX % 2) ++ { ++ colorInd = *tpChar >> 4; ++ ++tpChar; ++ } ++ else ++ colorInd = *tpChar & 0xF; ++ + if (mosH && i % mosH) + *ptr = ptr[-1]; + else + { +- if (flipH) +- if (tileX % 2) +- { +- colorInd = *tpChar & 0xF; +- --tpChar; +- } +- else +- colorInd = *tpChar >> 4; +- else +- if (tileX % 2) +- { +- colorInd = *tpChar >> 4; +- ++tpChar; +- } +- else +- colorInd = *tpChar & 0xF; +- + if (colorInd) + *ptr = pPalette[colorInd] | 0x8000; + else +@@ -309,7 +309,7 @@ namespace AMeteor + { + int32_t intX, intY; + +- uint8_t* pChar = m_memory.GetRealAddress(0x06000000); ++ uint16_t* pChar = (uint16_t*)m_memory.GetRealAddress(0x06000000); + + for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy) + { +@@ -318,25 +318,9 @@ namespace AMeteor + + // if we are off layer + if (intX < 0 || intX >= 240) +- if (m_cnt & (0x1 << 13)) +- { +- // NOTE : in C++, the modulus can be negative +- intX %= 240; +- if (intX < 0) +- intX += 240; +- } +- else +- continue; ++ continue; + if (intY < 0 || intY >= 160) +- if (m_cnt & (0x1 << 13)) +- { +- intY %= 160; +- if (intY < 0) +- intY += 160; +- } +- else +- continue; +- ++ continue; + *ptr = pChar[intY * 240 * 2 + intX * 2] | 0x8000; + } + } +@@ -413,6 +397,27 @@ namespace AMeteor + } + } + ++ void BgLayer::DrawLine5 (uint16_t* ptr, ++ int32_t curX, int32_t curY, ++ int16_t dx, int16_t dy, bool frame1) ++ { ++ int32_t intX, intY; ++ uint16_t* pChar = (uint16_t*) m_memory.GetRealAddress(frame1 ? 0x0600A000 : 0x06000000); ++ ++ for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy) ++ { ++ intX = curX >> 8; ++ intY = curY >> 8; ++ ++ // if we are off layer ++ if (intX < 0 || intX >= 160) ++ continue; ++ if (intY < 0 || intY >= 128) ++ continue; ++ *ptr = pChar[intY * 160 + intX] | 0x8000; ++ } ++ } ++ + void BgLayer::UpdateCnt (uint16_t cnt) + { + if (m_cnt == cnt) +diff --git a/ameteor/source/graphics/screen.cpp b/ameteor/source/graphics/screen.cpp +index 4c5628b..85da3b9 100644 +--- a/ameteor/source/graphics/screen.cpp ++++ b/ameteor/source/graphics/screen.cpp +@@ -121,6 +121,16 @@ namespace AMeteor + // if objects are enabled draw them + if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj); + break; ++ case 3: // bg2 only 15 bit direct color 240x160 ++ layersOn &= 0xF4; ++ if (layersOn & (0x1 << 2)) ++ m_bgLayer2.DrawLine3(lineBg+2*WIDTH, ++ m_refX2, m_refY2, ++ m_io.DRead16(Io::BG2PA), ++ m_io.DRead16(Io::BG2PC)); ++ if (layersOn & (0x1 << 4)) ++ m_objs.DrawLineHighOnly(line, lineObj); ++ break; + // TODO (remember, HIGH ONLY for objs, don't make shitty copy paste) + case 4: // bg2 only in mode 4 (bitmap 256) + layersOn &= 0xF4; +@@ -141,6 +151,18 @@ namespace AMeteor + // all objs with the current priority + m_objs.DrawLineHighOnly(line, lineObj); + break; ++ case 5: // bg2 only 15 bit direct color 160x128 2 frames ++ layersOn &= 0xF4; ++ if (layersOn & (0x1 << 2)) ++ m_bgLayer2.DrawLine5(lineBg+2*WIDTH, ++ m_refX2, m_refY2, ++ m_io.DRead16(Io::BG2PA), ++ m_io.DRead16(Io::BG2PC), ++ m_dispcnt & (0x1 << 4)); ++ ++ if (layersOn & (0x1 << 4)) ++ m_objs.DrawLineHighOnly(line, lineObj); ++ break; + default : + met_abort("not supported : " << (m_dispcnt & 0x7)); + break; +diff --git a/ameteor/source/io.cpp b/ameteor/source/io.cpp +index c0cd244..2003d21 100644 +--- a/ameteor/source/io.cpp ++++ b/ameteor/source/io.cpp +@@ -99,6 +99,8 @@ namespace AMeteor + // TODO implement unreadable or write-only io + uint8_t Io::Read8 (uint32_t add) + { ++ if ((add & 0xFFF) == KEYINPUT) ++ m_polled = true; + //debug ("IO Read8 at " << IOS_ADD << add << " of " << IOS_ADD << (int)*(uint8_t*)(m_iomem + (add & 0xFFF))); + if ((add & 0xFF0) == 0x100) + switch (add & 0xF) +@@ -114,6 +116,8 @@ namespace AMeteor + + uint16_t Io::Read16 (uint32_t add) + { ++ if ((add & 0xFFE) == KEYINPUT) ++ m_polled = true; + //debug ("IO Read16 at " << IOS_ADD << add << " of " << IOS_ADD << *(uint16_t*)(m_iomem + (add & 0xFFF))); + // special case, reading timers + if ((add & 0xFF0) == 0x100) +@@ -129,6 +133,8 @@ namespace AMeteor + + uint32_t Io::Read32 (uint32_t add) + { ++ if ((add & 0xFFC) == KEYINPUT) ++ m_polled = true; + //debug ("IO Read32 at " << IOS_ADD << add << " of " << IOS_ADD << *(uint32_t*)(m_iomem + (add & 0xFFF))); + // special case, reading timers + if ((add & 0xFF0) == 0x100) +@@ -282,29 +288,36 @@ namespace AMeteor + // update the vcounter flag and eventually trigger an interrupt + W16(add, (val & 0xFFF8) | (m_iomem[add & 0xFFF] & 0x07)); + break; +- // The BG*OFS are write-only, we don't need to W16() + case BG0HOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg0XOff(val & 0x1FF); + break; + case BG0VOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg0YOff(val & 0x1FF); + break; + case BG1HOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg1XOff(val & 0x1FF); + break; + case BG1VOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg1YOff(val & 0x1FF); + break; + case BG2HOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg2XOff(val & 0x1FF); + break; + case BG2VOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg2YOff(val & 0x1FF); + break; + case BG3HOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg3XOff(val & 0x1FF); + break; + case BG3VOFS: ++ W16(add, val & 0x1FF); + LCD.UpdateBg3YOff(val & 0x1FF); + break; + case BG2X_H: +diff --git a/ameteor/source/memory.cpp b/ameteor/source/memory.cpp +index bf17688..839841e 100644 +--- a/ameteor/source/memory.cpp ++++ b/ameteor/source/memory.cpp +@@ -464,8 +464,9 @@ namespace AMeteor + // write if we have a custom bios and write it too + bool b = m_brom; + SS_WRITE_VAR(b); +- if (b) ++ if (b) { + SS_WRITE_DATA(m_brom, 0x00004000); ++ } + SS_WRITE_DATA(m_wbram, 0x00040000); + SS_WRITE_DATA(m_wcram, 0x00008000); + SS_WRITE_DATA(m_pram , 0x00000400); +@@ -490,9 +491,9 @@ namespace AMeteor + // read if we have a custom bios and write it too + bool b; + SS_READ_VAR(b); +- if (b) ++ if (b) { + SS_READ_DATA(m_brom , 0x00004000); +- else ++ } else + UnloadBios(); + SS_READ_DATA(m_wbram, 0x00040000); + SS_READ_DATA(m_wcram, 0x00008000); +@@ -532,6 +533,8 @@ namespace AMeteor + if (!r) + { + debugm("Unknown address for Read8 : " << IOS_ADD << add); ++ if(add == R(15)) ++ return 0xFF; //We are fucked... + // FIXME : in arm state, vba returns read8(r15 + (add & 3)) + // and in thumb read8(r15 + (add & 1)) + return Read8(R(15)); +@@ -559,8 +562,10 @@ namespace AMeteor + if (!r) + { + debugm("Unknown address for Read16 : " << IOS_ADD << add); +- if (R(15) == add) ++ if (R(15) == add) { + met_abort("Illegal PC"); ++ return 0xFFFF; //We are fucked. ++ } + // FIXME : in arm state, vba returns read16(r15 + (add & 2)) + return Read16(R(15)); + } +@@ -586,8 +591,10 @@ namespace AMeteor + if (!r) + { + debugm("Unknown address for Read32 : " << IOS_ADD << add); +- if (R(15) == add) ++ if (R(15) == add) { + met_abort("Illegal PC"); ++ return 0xFFFFFFFFU; //We are fucked. ++ } + if (FLAG_T) + { + uint16_t o = Read16(R(15)); +@@ -810,4 +817,20 @@ namespace AMeteor + if (m_cart->Write(add, val)) + CLOCK.SetBattery(CART_SAVE_TIME); + } ++ ++ std::map > Memory::GetMemories() ++ { ++ std::map > x; ++ if(m_brom) ++ x["brom"] = std::make_pair(m_brom, 0x00004000); ++ x["wbram"] = std::make_pair(m_wbram, 0x00040000); ++ x["wcram"] = std::make_pair(m_wcram, 0x00008000); ++ x["pram"] = std::make_pair(m_pram, 0x00000400); ++ x["vram"] = std::make_pair(m_vram, 0x00018000); ++ x["oam"] = std::make_pair(m_oram, 0x00000400); ++ if(m_cart) ++ x["sram"] = std::make_pair(m_cart->GetRawData(), m_cart->GetRawSize()); ++ x["rom"] = std::make_pair(m_rom, 0x02000000); ++ return x; ++ } + } +-- +1.7.9.48.g85da4d + diff --git a/options.build b/options.build index 1b87cca3..0995caa4 100644 --- a/options.build +++ b/options.build @@ -106,6 +106,9 @@ BUILD_GAMBATTE= # Set to non-empty value (e.g. 'yes') to build the bsnes core. BUILD_BSNES=yes +# Set to non-empty value (e.g. 'yes') to build the meteor core. +BUILD_METEOR= + # Set to non-empty value (e.g. 'yes') if bsnes build uses compatiblity core, not accuracy core. BSNES_IS_COMPAT= diff --git a/src/emulation/Makefile b/src/emulation/Makefile index 7a9b1a34..9f4e0a5b 100644 --- a/src/emulation/Makefile +++ b/src/emulation/Makefile @@ -1,4 +1,4 @@ -CORES=bsnes-legacy gambatte sky test +CORES=bsnes-legacy gambatte sky test meteor ALLFILES=__all__.files ALLFLAGS=__all__.ldflags CORES_FILES=$(patsubst %,%/$(ALLFILES),$(CORES)) @@ -23,6 +23,9 @@ sky/$(ALLFILES): forcelook make-ports.exe test/$(ALLFILES): forcelook make-ports.exe $(MAKE) -C test +meteor/$(ALLFILES): forcelook + $(MAKE) -C meteor + .PRECIOUS: %.$(OBJECT_SUFFIX) %.files precheck: @@ -30,11 +33,13 @@ precheck: $(MAKE) -C gambatte precheck $(MAKE) -C sky precheck $(MAKE) -C test precheck + $(MAKE) -C meteor precheck clean: rm -f *.$(OBJECT_SUFFIX) __all__.ldflags __all__.files $(MAKE) -C bsnes-legacy clean $(MAKE) -C gambatte clean + $(MAKE) -C meteor clean $(MAKE) -C sky clean $(MAKE) -C test clean rm -f make-ports.exe diff --git a/src/emulation/meteor/Makefile b/src/emulation/meteor/Makefile new file mode 100644 index 00000000..26b2afb1 --- /dev/null +++ b/src/emulation/meteor/Makefile @@ -0,0 +1,42 @@ +ifdef BUILD_METEOR +OBJECTS=core.$(OBJECT_SUFFIX) +METEOR_CFLAGS= +METEOR_LDFLAGS= +REALAR=$(CROSS_PREFIX)ar +export REALAR +METEOR_LIBRARY=meteor/libmeteor.$(ARCHIVE_SUFFIX) + +.PRECIOUS: %.$(OBJECT_SUFFIX) %.files + +__all__.files: $(OBJECTS) ../../../$(METEOR_LIBRARY) + lua ../../genfilelist.lua $^ >$@ + echo $(METEOR_LDFLAGS) ../$(METEOR_LIBRARY) >__all__.ldflags + +../../../$(METEOR_LIBRARY): forcelook + $(MAKE) -C ../../../meteor + $(REALRANLIB) $@ + +ports.inc: ports.json ../make-ports.exe + ../make-ports.exe <$< >$@ + +%.$(OBJECT_SUFFIX): %.cpp ports.inc + $(REALCC) -c -o $@ $< -I../../../include -I../../../meteor/ameteor/include $(CFLAGS) $(METEOR_CFLAGS) + +else + +OBJECTS= +__all__.files: $(OBJECTS) + lua ../../genfilelist.lua $^ >$@ + echo >__all__.ldflags + +endif + +forcelook: + @true + +precheck: + @true + +clean: + -make -C ../../../gambatte clean + rm -f *.$(OBJECT_SUFFIX) __all__.ldflags __all__.files diff --git a/src/emulation/meteor/core.cpp b/src/emulation/meteor/core.cpp new file mode 100644 index 00000000..79d0ee99 --- /dev/null +++ b/src/emulation/meteor/core.cpp @@ -0,0 +1,497 @@ +/*************************************************************************** + * Copyright (C) 2013 by Ilari Liusvaara * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ +#include "lsnes.hpp" +#include +#include +#include +#include +#include +#include "core/audioapi.hpp" +#include "core/misc.hpp" +#include "core/command.hpp" +#include "core/controllerframe.hpp" +#include "core/dispatch.hpp" +#include "core/framebuffer.hpp" +#include "core/window.hpp" +#include "interface/callbacks.hpp" +#include "interface/cover.hpp" +#include "interface/romtype.hpp" +#include "library/framebuffer-pixfmt-rgb15.hpp" +#include "library/string.hpp" +//#include "library/portfn.hpp" +#include "library/serialization.hpp" +#include "library/minmax.hpp" +#include "library/framebuffer.hpp" +#define __LIBRETRO__ +#include "ameteor.hpp" +#include "ameteor/cartmem.hpp" + +uint64_t get_utime(); + +namespace +{ + bool do_reset_flag = false; + bool internal_rom = false; + bool rtc_fixed; + time_t rtc_fixed_val; + std::vector romdata; + uint16_t cover_fbmem[720 * 480]; + bool pflag = false; + int16_t soundbuf[65536]; + size_t soundbuf_fill; + bool frame_happened; + bool just_reset; + + struct interface_device_reg gba_registers[] = { + {NULL, NULL, NULL} + }; + + //Framebuffer. + struct framebuffer::info cover_fbinfo = { + &framebuffer::pixfmt_rgb15, //Format. + (char*)cover_fbmem, //Memory. + 720, 480, 1440, //Physical size. + 720, 480, 1440, //Logical size. + 0, 0 //Offset. + }; + +#include "ports.inc" + + struct _output + { + void frame(const uint16_t* p) + { + AMeteor::Stop(); + framebuffer::info inf; + inf.type = &framebuffer::pixfmt_rgb15; + inf.mem = const_cast(reinterpret_cast(p)); + inf.physwidth = 240; + inf.physheight = 160; + inf.physstride = 480; + inf.width = 240; + inf.height = 160; + inf.stride = 480; + inf.offset_x = 0; + inf.offset_y = 0; + + framebuffer::raw ls(inf); + ecore_callbacks->output_frame(ls, 262144, 4389); + ecore_callbacks->timer_tick(4389, 262144); + static uint32_t refreshes = 0; + static uint64_t samples = 0; + refreshes++; + static double srate = 4194304.0/95.0; + if(soundbuf_fill > 0) { + samples += soundbuf_fill / 2; + audioapi_submit_buffer(soundbuf, soundbuf_fill / 2, true, srate); + soundbuf_fill = 0; + } + frame_happened = true; + } + void sample(const int16_t* s) + { + soundbuf[soundbuf_fill++] = s[0]; + soundbuf[soundbuf_fill++] = s[1]; + } + void vblank() + { + } + } output; + + std::string fmt_sram_size(uint32_t sram_size) + { + std::string mult = ""; + if(sram_size > 1024) { + mult = "k"; + sram_size >>= 10; + } + if(sram_size > 1024) { + mult = "M"; + sram_size >>= 10; + } + return (stringfmt() << sram_size << mult << "B").str(); + } + + void meteor_bus_write(uint64_t offset, uint8_t data) + { + uint8_t* m = AMeteor::_memory.GetRealAddress(offset); + if(m) *m = data; + } + + uint8_t meteor_bus_read(uint64_t offset) + { + uint8_t* m = AMeteor::_memory.GetRealAddress(offset); + return m ? *m : 0xFF; + } + + void avsync_hack() + { + //Hack: Send 223 samples. + for(unsigned i = 0; i < 446; i++) + soundbuf[soundbuf_fill++] = 0; + } + + void meteor_poll_buttons() + { + uint16_t x = 0; + for(unsigned i = 0; i < 10; i++) + if(ecore_callbacks->get_input(0, 1, i)) + x |= (1 << i); + AMeteor::_keypad.SetPadState(x ^ 0x3FF); //Inverse polarity. + pflag = true; + } + + void basic_init() + { + static bool done = false; + if(done) + return; + done = true; + AMeteor::_memory.LoadCartInferred(); + AMeteor::_lcd.sig_vblank.connect(syg::mem_fun(output, &_output::vblank)); + AMeteor::_lcd.GetScreen().GetRenderer().SetFrameSlot(syg::mem_fun(output, &_output::frame)); + AMeteor::_sound.GetSpeaker().SetFrameSlot(syg::mem_fun(output, &_output::sample)); + } + + controller_set meteor_controllerconfig(std::map& settings) + { + std::map _settings = settings; + controller_set r; + r.ports.push_back(&psystem); + r.logical_map.push_back(std::make_pair(0, 1)); + return r; + } + + std::pair meteor_bus_map() + { + return std::make_pair(0x100000000ULL, 0x100000000ULL); + } + + void add_vma_mapped(std::list& l, const std::string& name, uint64_t base, uint8_t* ram, + size_t ramsize, int endian, bool readonly) + { + core_vma_info v; + v.name = name; + v.base = base; + v.size = ramsize; + v.backing_ram = ram; + v.endian = endian; + v.readonly = readonly; + v.read = NULL; + v.write = NULL; + l.push_back(v); + } + + uint64_t get_vmabase(const std::string& name) + { + static std::map unknown; + uint64_t unknown_next = 0x200000000ULL; + if(name == "brom") return 0x90000000ULL; + if(name == "wbram") return 0; + if(name == "wcram") return 0x40000ULL; + if(name == "pram") return 0x100000ULL; + if(name == "vram") return 0x110000ULL; + if(name == "oam") return 0x120000ULL; + if(name == "sram") return 0x10000000ULL; + if(name == "rom") return 0x80000000ULL; + //Unknown. + if(!unknown.count(name)) { + unknown[name] = unknown_next; + unknown_next += 0x100000000ULL; + } + return unknown[name]; + } + + bool is_read_only_vma(const std::string& name) + { + if(name == "wbram") return false; + if(name == "wcram") return false; + if(name == "pram") return false; + if(name == "vram") return false; + if(name == "oam") return false; + if(name == "sram") return false; + //Dunno what this is. + return true; + } + + std::list get_VMAlist() + { + std::list vmas; + if(!internal_rom) + return vmas; + auto mem_map = AMeteor::_memory.GetMemories(); + for(auto i : mem_map) + add_vma_mapped(vmas, i.first, get_vmabase(i.first), i.second.first, i.second.second, -1, + is_read_only_vma(i.first)); + //Bus mapping. + core_vma_info bus; + bus.name = "BUS"; + bus.base = 0x100000000ULL; + bus.size = 0x100000000ULL; + bus.backing_ram = NULL; + bus.endian = -1; + bus.readonly = false; + bus.read = meteor_bus_read; + bus.write = meteor_bus_write; + vmas.push_back(bus); + + return vmas; + } + + std::set meteor_srams() + { + std::set s; + if(!internal_rom) + return s; + auto mem_map = AMeteor::_memory.GetMemories(); + if(mem_map.count("sram")) + s.insert("sram"); + return s; + } + + std::string get_cartridge_name() + { + std::ostringstream name; + if(romdata.size() < 192) + return ""; //Bad. + for(unsigned i = 0; i < 12; i++) { + if(romdata[0x0A0 + i]) + name << (char)romdata[0xA0 + i]; + else + break; + } + name << "[AGB-"; + for(unsigned i = 0; i < 4; i++) + name << (char)romdata[0xAC + i]; + name << "] (version " << (int)(unsigned char)romdata[0xBC]; + if((unsigned char)romdata[0x9C] == 0xA5) + name << "-Debug"; + name << ")"; + return name.str(); + } + + void redraw_cover_fbinfo(); + + struct _meteor_core : public core_core, public core_type, public core_region, public core_sysregion + { + _meteor_core() + : core_core({&psystem}, {{0, "Soft reset", "reset", {}}}), + core_type({{ + .iname = "agb", + .hname = "Game Boy Advance", + .id = 0, + .sysname = "GBA", + .bios = NULL, + .regions = {this}, + .images = {{"rom", "Cartridge ROM", 1, 0, 0, "gba;agb"}}, + .settings = {{"extbios", "Use GBA BIOS", "0", { + {"0", "False", 0}, + {"1", "True", 1} + }}}, + .core = this, + }}), + core_region({{"world", "World", 0, 0, false, {4389, 262144}, {0}}}), + core_sysregion("magb", *this, *this) {} + + std::string c_core_identifier() { return "meteor 1.4.0"; } + bool c_set_region(core_region& region) { return (®ion == this); } + std::pair c_video_rate() { return std::make_pair(262144, 4389); } + std::pair c_audio_rate() { return std::make_pair(4194304, 95); } + std::map> c_save_sram() throw(std::bad_alloc) { + std::map> s; + if(!internal_rom) + return s; + std::vector sram; + uint32_t realsize = *(uint32_t*)(AMeteor::CartMemData + AMeteor::CartMem::MAX_SIZE); + sram.resize(realsize); + memcpy(&sram[0], AMeteor::CartMemData, realsize); + s["sram"] = sram; + return s; + } + void c_load_sram(std::map>& sram) throw(std::bad_alloc) { + if(!internal_rom) + return; + if(!sram.count("sram")) { + //Don't clear the SRAM descriptor. + memset(AMeteor::CartMemData, 255, AMeteor::CartMem::MAX_SIZE); + return; + } + std::vector& s = sram["sram"]; + //Read the size from SRAM descriptor. + uint32_t realsize = *(uint32_t*)(AMeteor::CartMemData + AMeteor::CartMem::MAX_SIZE); + //Don't clear the SRAM descriptor. + memset(AMeteor::CartMemData, 255, AMeteor::CartMem::MAX_SIZE); + if(s.size() != realsize) + messages << "Unexpected SRAM size, expected " << realsize << " got " << s.size() + << std::endl; + memcpy(AMeteor::CartMemData, &s[0], min((size_t)realsize, s.size())); + } + void c_serialize(std::vector& out) { + if(!internal_rom) + throw std::runtime_error("Can't save without ROM"); + std::ostringstream stream; + AMeteor::SaveState(stream); + std::string s = stream.str(); + out.resize(s.length()); + std::copy(s.begin(), s.end(), out.begin()); + } + void c_unserialize(const char* in, size_t insize) { + if(!internal_rom) + throw std::runtime_error("Can't load without ROM"); + std::istringstream stream; + stream.str(std::string((char*)in, insize)); + AMeteor::LoadState(stream); + do_reset_flag = false; + } + core_region& c_get_region() { return *this; } + void c_power() {} + void c_unload_cartridge() {} + std::pair c_get_scale_factors(uint32_t width, uint32_t height) { + return std::make_pair(max(720 / width, (uint32_t)1), max(480 / height, (uint32_t)1)); + } + void c_install_handler() { magic_flags |= 4; } + void c_uninstall_handler() {} + void c_emulate() { + if(!internal_rom) + return; + int16_t reset = ecore_callbacks->get_input(0, 0, 1); + if(reset) { + AMeteor::Reset(AMeteor::UNIT_ALL & ~AMeteor::UNIT_MEMORY_BIOS & + ~AMeteor::UNIT_MEMORY_ROM); + just_reset = true; + messages << "GBA reset" << std::endl; + } + do_reset_flag = false; + meteor_poll_buttons(); + frame_happened = false; + if(just_reset) { + avsync_hack(); + just_reset = false; + } + while(!frame_happened) { + AMeteor::Run(281000); + if(!frame_happened) + std::cerr << "Warning: Run timeout" << std::endl; + } + } + void c_runtosave() {} + bool c_get_pflag() { return AMeteor::_io.GetPolled(); } + void c_set_pflag(bool _pflag) { AMeteor::_io.SetPolled(_pflag); } + void c_request_reset(long delay, bool hard) { do_reset_flag = true; } + framebuffer::raw& c_draw_cover() { + static framebuffer::raw x(cover_fbinfo); + redraw_cover_fbinfo(); + return x; + } + std::string c_get_core_shortname() { return "meteor140"; } + void c_pre_emulate_frame(controller_frame& cf) { + cf.axis3(0, 0, 1, do_reset_flag ? 1 : 0); + } + void c_execute_action(unsigned id, const std::vector& p) + { + if(id == 0) + do_reset_flag = true; + } + unsigned int c_action_flags(unsigned id) + { + if(id == 0) return 1; + return 0; + } + const interface_device_reg* c_get_registers() { return gba_registers; } + int c_reset_action(bool hard) { return hard ? -1 : 0; } + int t_load_rom(core_romimage* img, std::map& settings, uint64_t rtc_sec, + uint64_t rtc_subsec) { + uint32_t sram_size = 0; + std::map _settings = settings; + get_settings().fill_defaults(_settings); + basic_init(); + const char* markup = img[0].markup; + if(!markup) + markup = ""; + std::string _markup = markup; + std::istringstream imarkup(markup); + const unsigned char* data = img[0].data; + size_t size = img[0].size; + romdata.resize(size); + memcpy(&romdata[0], data, size); + std::string markup_line; + + while(std::getline(imarkup, markup_line)) { + regex_results r; + istrip_CR(markup_line); + if(r = regex("sram_size=([0-9]+)", markup_line)) { + try { + sram_size = parse_value(r[1]); + } catch(...) { + } + } else if(r = regex("sram_size=([0-9]+)k", markup_line)) { + try { + sram_size = 1024 * parse_value(r[1]); + } catch(...) { + } + } else + messages << "Unknown markup: " << markup_line << std::endl; + } + + AMeteor::Reset(AMeteor::UNIT_ALL); + if(_settings["extbios"] != "0") { + //Load the BIOS. + std::string bname = ecore_callbacks->get_firmware_path() + "/gbabios.bin"; + if(!AMeteor::_memory.LoadBios(bname.c_str())) { + messages << "Can't load GBA BIOS" << std::endl; + return -1; + } + } + AMeteor::_memory.LoadRom((const uint8_t*)data, size); + *(uint32_t*)(AMeteor::CartMemData + AMeteor::CartMem::MAX_SIZE) = sram_size; + if(sram_size > 0) + messages << "SRAM size: " << fmt_sram_size(sram_size) << std::endl; + AMeteor::_memory.LoadCartInferred(); + internal_rom = true; + do_reset_flag = false; + just_reset = true; + return 0; + } + controller_set t_controllerconfig(std::map& settings) + { + return meteor_controllerconfig(settings); + } + std::pair c_get_bus_map() { return meteor_bus_map(); } + std::list c_vma_list() { return get_VMAlist(); } + std::set c_srams() { return meteor_srams(); } + double c_get_PAR() { return 1.0; } + void c_set_debug_flags(uint64_t addr, unsigned flags_set, unsigned flags_clear) {} + void c_set_cheat(uint64_t addr, uint64_t value, bool set) {} + std::vector c_get_trace_cpus() { return std::vector(); } + void c_debug_reset() {} + } meteor_core; + + void redraw_cover_fbinfo() + { + for(size_t i = 0; i < sizeof(cover_fbmem) / sizeof(cover_fbmem[0]); i++) + cover_fbmem[i] = 0x0000; + std::string ident = meteor_core.c_core_identifier(); + cover_render_string(cover_fbmem, 0, 0, ident, 0xFFFF, 0x0000, 720, 480, 1440, 2); + cover_render_string(cover_fbmem, 0, 16, "Internal ROM name: " + get_cartridge_name(), 0xFFFF, 0x0000, + 720, 480, 1440, 2); + unsigned y = 32; + for(auto i : cover_information()) { + cover_render_string(cover_fbmem, 0, y, i, 0xFFFF, 0x0000, 720, 480, 1440, 2); + y += 16; + } + } +} diff --git a/src/emulation/meteor/ports.json b/src/emulation/meteor/ports.json new file mode 100644 index 00000000..2b1fb21e --- /dev/null +++ b/src/emulation/meteor/ports.json @@ -0,0 +1,25 @@ +{ + "ports":[{ + "symbol":"psystem", "name":"system", "hname":"system","controllers":[{ + "type":"(system)", "class":"(system)","buttons":[ + {"type":"button","symbol":"F","name":"framesync", "shadow":true}, + {"type":"button","symbol":"R","name":"reset", "shadow":true}, + {"type":"null", "shadow":true}, + {"type":"null", "shadow":true} + ] + },{ + "type":"gamepad", "class":"gba","buttons":[ + {"type":"button","name":"A"}, + {"type":"button","name":"B"}, + {"type":"button","symbol":"s","name":"select"}, + {"type":"button","symbol":"S","name":"start"}, + {"type":"button","movie":"r","symbol":"→","name":"right","macro":">"}, + {"type":"button","movie":"l","symbol":"←","name":"left","macro":"<"}, + {"type":"button","movie":"u","symbol":"↑","name":"up","macro":"^"}, + {"type":"button","movie":"d","symbol":"↓","name":"down","macro":"v"}, + {"type":"button","name":"L"}, + {"type":"button","name":"R"} + ] + }],"legal":[0] + }] +}