Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2020-02-17 17:46:29 -08:00
commit e5c6373f88
43 changed files with 1213 additions and 554 deletions

View File

@ -1,7 +1,7 @@
#!/bin/sh
if [ $TRAVIS_OS_NAME = "osx" ]; then
brew update
brew install qt5 ffmpeg sdl2 libedit libelf libpng libzip
brew install qt5 ffmpeg sdl2 libelf libpng libzip
else
sudo apt-get update
sudo apt-get -y install libseccomp2

45
CHANGES
View File

@ -19,34 +19,65 @@ Misc:
- DS Video: Simplify VRAM mapping
0.9.0: (Future)
Features:
- Add APNG recording
Emulation fixes:
- ARM: Fix ALU reading PC after shifting
- ARM: Fix STR storing PC after address calculation
- GBA BIOS: Implement dummy sound driver calls
- GBA BIOS: Improve HLE BIOS timing
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
- GBA Memory: Misaligned SRAM writes are ignored
- GBA Memory: Improve gamepak prefetch timing
- GBA Serialize: Fix serializing DMA transfer register
- GBA Serialize: Fix audio serialization for desynced FIFOs
- GBA Serialize: Fix audio DMA timing deserialization
- GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630)
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
- GBA Video: Fix Hblank timing
Other fixes:
- Core: Ensure ELF regions can be written before trying
- Core: Fix ELF loading regression (fixes mgba.io/i/1669)
- Debugger: Don't skip undefined instructions when debugger attached
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
Misc:
- FFmpeg: Add looping option for GIF/APNG
- Qt: Renderer can be changed while a game is running
- Qt: Add hex index to palette view
- Qt: Add transformation matrix info to sprite view
0.8.1: (2020-02-16)
Emulation fixes:
- GB Serialize: Fix timing bug loading channel 4 timing
- GBA: Fix multiboot entry point while skipping BIOS
- GBA BIOS: Fix undefined instruction HLE behavior
- GBA DMA: Fix invalid audio DMA parameters
- GBA Memory: Misaligned SRAM writes are ignored
- GBA Serialize: Fix serializing DMA transfer register
- GBA Serialize: Fix audio DMA timing deserialization
- GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630)
- GBA Video: Fix backdrop blending on lines without sprites (fixes mgba.io/i/1647)
- GBA Video: Fix OpenGL sprite flag priority
Other fixes:
- Core: Fix race condition initializing thread proxy
- Core: Fix integer overflow in ELF loading
- FFmpeg: Fix crash when -strict -2 is needed for vcodec or container
- FFmpeg: Disallow recording video with no audio nor video
- GBA: Automatically skip BIOS for multiboot ROMs
- Qt: Only dynamically reset video scale if a game is running
- Qt: Fix race condition with proxied video events
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
- Qt: Fix color selection in asset view (fixes mgba.io/i/1648)
- Qt: Fix missing OSD messages
- Qt: Fix crash unloading shaders
- Qt: Fix toggled actions on gamepads (fixes mgba.io/i/1650)
- Qt: Fix extraneous dialog (fixes mgba.io/i/1654)
- Qt: Fix window title not updating after shutting down game
- Qt: Fix GIF view not allowing manual filename entry
- Qt: Fix non-GB build (fixes mgba.io/i/1664)
- Qt: Fix pausing Qt Multimedia audio (fixes mgba.io/i/1643)
- Qt: Fix invalid names for modifier keys (fixes mgba.io/i/525)
- SDL: Refresh stale pointers after adding a joystick (fixes mgba.io/i/1622)
- Util: Fix crash reading invalid ELFs
- VFS: Fix handle leak when double-mapping (fixes mgba.io/i/1659)
Misc:
- Qt: Renderer can be changed while a game is running
- FFmpeg: Add more presets
- Qt: Fix non-SDL build (fixes mgba.io/i/1656)
- SDL: Use DirectSound audio driver by default on Windows
- Switch: Make OpenGL scale adjustable while running
0.8.0: (2020-01-21)

View File

@ -870,6 +870,9 @@ if(NOT SKIP_LIBRARY)
target_link_libraries(${BINARY_NAME} ${DEBUGGER_LIB} ${DEPENDENCY_LIB} ${OS_LIB})
install(TARGETS ${BINARY_NAME} LIBRARY DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME} NAMELINK_SKIP ARCHIVE DESTINATION ${LIBDIR} RUNTIME DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME})
if(BUILD_SHARED)
install(TARGETS ${BINARY_NAME} LIBRARY DESTINATION ${LIBDIR} COMPONENT ${BINARY_NAME}-dev NAMELINK_ONLY)
endif()
if(UNIX AND NOT APPLE AND NOT HAIKU)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/mgba-16.png DESTINATION share/icons/hicolor/16x16/apps RENAME mgba.png COMPONENT lib${BINARY_NAME})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/mgba-24.png DESTINATION share/icons/hicolor/24x24/apps RENAME mgba.png COMPONENT lib${BINARY_NAME})

View File

@ -20,15 +20,21 @@ CXX_GUARD_START
#define SOCKET_FAILED(s) ((s) == INVALID_SOCKET)
typedef SOCKET Socket;
#else
#ifdef GEKKO
#include <network.h>
#else
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/select.h>
#include <sys/socket.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#ifndef GEKKO
#define INVALID_SOCKET (-1)
#endif
#define SOCKET_FAILED(s) ((s) < 0)
typedef int Socket;
#endif
@ -70,6 +76,8 @@ static inline void SocketSubsystemInit() {
}
#elif defined(__SWITCH__)
socketInitializeDefault();
#elif defined(GEKKO)
net_init();
#endif
}
@ -82,6 +90,8 @@ static inline void SocketSubsystemDeinit() {
SOCUBuffer = NULL;
#elif defined(__SWITCH__)
socketExit();
#elif defined(GEKKO)
net_deinit();
#endif
}
@ -104,6 +114,8 @@ static inline bool SocketWouldBlock() {
static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) {
#ifdef _WIN32
return send(socket, (const char*) buffer, size, 0);
#elif defined(GEKKO)
return net_write(socket, buffer, size);
#else
return write(socket, buffer, size);
#endif
@ -112,6 +124,8 @@ static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size)
static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
#if defined(_WIN32) || defined(__SWITCH__)
return recv(socket, (char*) buffer, size, 0);
#elif defined(GEKKO)
return net_read(socket, buffer, size);
#else
return read(socket, buffer, size);
#endif
@ -120,13 +134,19 @@ static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
static inline int SocketClose(Socket socket) {
#ifdef _WIN32
return closesocket(socket) == 0;
#elif defined(GEKKO)
return net_close(socket) >= 0;
#else
return close(socket) >= 0;
#endif
}
static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) {
#ifdef GEKKO
Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
#else
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
#endif
if (SOCKET_FAILED(sock)) {
return sock;
}
@ -142,15 +162,23 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress)
#else
bindInfo.sin_addr.s_addr = gethostid();
#endif
#ifdef GEKKO
err = net_bind(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
#else
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
#endif
} else if (bindAddress->version == IPV4) {
struct sockaddr_in bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin_family = AF_INET;
bindInfo.sin_port = htons(port);
bindInfo.sin_addr.s_addr = htonl(bindAddress->ipv4);
#ifdef GEKKO
err = net_bind(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
#else
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
#ifndef _3DS
#endif
#if !defined(_3DS) && !defined(GEKKO)
} else {
struct sockaddr_in6 bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
@ -168,7 +196,11 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress)
}
static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) {
#ifdef GEKKO
Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
#else
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
#endif
if (SOCKET_FAILED(sock)) {
return sock;
}
@ -179,15 +211,23 @@ static inline Socket SocketConnectTCP(int port, const struct Address* destinatio
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin_family = AF_INET;
bindInfo.sin_port = htons(port);
#ifdef GEKKO
err = net_connect(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
#else
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
#endif
} else if (destinationAddress->version == IPV4) {
struct sockaddr_in bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin_family = AF_INET;
bindInfo.sin_port = htons(port);
bindInfo.sin_addr.s_addr = htonl(destinationAddress->ipv4);
#ifdef GEKKO
err = net_connect(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
#else
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
#ifndef _3DS
#endif
#if !defined(_3DS) && !defined(GEKKO)
} else {
struct sockaddr_in6 bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
@ -206,12 +246,23 @@ static inline Socket SocketConnectTCP(int port, const struct Address* destinatio
}
static inline Socket SocketListen(Socket socket, int queueLength) {
#ifdef GEKKO
return net_listen(socket, queueLength);
#else
return listen(socket, queueLength);
#endif
}
static inline Socket SocketAccept(Socket socket, struct Address* address) {
if (!address) {
#ifdef GEKKO
struct sockaddr_in addrInfo;
memset(&addrInfo, 0, sizeof(addrInfo));
socklen_t len = sizeof(addrInfo);
return net_accept(socket, (struct sockaddr*) &addrInfo, &len);
#else
return accept(socket, 0, 0);
#endif
}
if (address->version == IPV4) {
struct sockaddr_in addrInfo;
@ -219,8 +270,12 @@ static inline Socket SocketAccept(Socket socket, struct Address* address) {
addrInfo.sin_family = AF_INET;
addrInfo.sin_addr.s_addr = address->ipv4;
socklen_t len = sizeof(addrInfo);
#ifdef GEKKO
return net_accept(socket, (struct sockaddr*) &addrInfo, &len);
#else
return accept(socket, (struct sockaddr*) &addrInfo, &len);
#ifndef _3DS
#endif
#if !defined(_3DS) && !defined(GEKKO)
} else {
struct sockaddr_in6 addrInfo;
memset(&addrInfo, 0, sizeof(addrInfo));
@ -237,8 +292,12 @@ static inline int SocketSetBlocking(Socket socket, bool blocking) {
#ifdef _WIN32
u_long unblocking = !blocking;
return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR;
#else
#ifdef GEKKO
int flags = net_fcntl(socket, F_GETFL, 0);
#else
int flags = fcntl(socket, F_GETFL);
#endif
if (flags == -1) {
return 0;
}
@ -247,12 +306,20 @@ static inline int SocketSetBlocking(Socket socket, bool blocking) {
} else {
flags |= O_NONBLOCK;
}
#ifdef GEKKO
return net_fcntl(socket, F_SETFL, flags) >= 0;
#else
return fcntl(socket, F_SETFL, flags) >= 0;
#endif
#endif
}
static inline int SocketSetTCPPush(Socket socket, int push) {
#ifdef GEKKO
return net_setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
#else
return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
#endif
}
static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) {
@ -304,7 +371,11 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc
struct timeval tv;
tv.tv_sec = timeoutMillis / 1000;
tv.tv_usec = (timeoutMillis % 1000) * 1000;
#ifdef GEKKO
int result = net_select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
#else
int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
#endif
int r = 0;
int w = 0;
int e = 0;

View File

@ -208,6 +208,7 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc);
void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size);
void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask);
#ifdef USE_ELF
struct ELF;

View File

@ -176,6 +176,7 @@ enum mCoreMemoryBlockFlags {
mCORE_MEMORY_READ = 0x01,
mCORE_MEMORY_WRITE = 0x02,
mCORE_MEMORY_RW = 0x03,
mCORE_MEMORY_WORM = 0x04,
mCORE_MEMORY_MAPPED = 0x10,
mCORE_MEMORY_VIRTUAL = 0x20,
};

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
/* Copyright (c) 2013-2019 Jeffrey Pfau
*
* 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
@ -24,7 +24,6 @@ enum GBKey {
GB_KEY_UP = 6,
GB_KEY_DOWN = 7,
GB_KEY_MAX,
GB_KEY_NONE = -1
};
CXX_GUARD_END

View File

@ -14,6 +14,52 @@ CXX_GUARD_START
mLOG_DECLARE_CATEGORY(GBA_BIOS);
enum GBASwi {
GBA_SWI_SOFT_RESET = 0x00,
GBA_SWI_REGISTER_RAM_RESET = 0x01,
GBA_SWI_HALT = 0x02,
GBA_SWI_STOP = 0x03,
GBA_SWI_INTR_WAIT = 0x04,
GBA_SWI_VBLANK_INTR_WAIT = 0x05,
GBA_SWI_DIV = 0x06,
GBA_SWI_DIV_ARM = 0x07,
GBA_SWI_SQRT = 0x08,
GBA_SWI_ARCTAN = 0x09,
GBA_SWI_ARCTAN2 = 0x0A,
GBA_SWI_CPU_SET = 0x0B,
GBA_SWI_CPU_FAST_SET = 0x0C,
GBA_SWI_GET_BIOS_CHECKSUM = 0x0D,
GBA_SWI_BG_AFFINE_SET = 0x0E,
GBA_SWI_OBJ_AFFINE_SET = 0x0F,
GBA_SWI_BIT_UNPACK = 0x10,
GBA_SWI_LZ77_UNCOMP_WRAM = 0x11,
GBA_SWI_LZ77_UNCOMP_VRAM = 0x12,
GBA_SWI_HUFFMAN_UNCOMP = 0x13,
GBA_SWI_RL_UNCOMP_WRAM = 0x14,
GBA_SWI_RL_UNCOMP_VRAM = 0x15,
GBA_SWI_DIFF_8BIT_UNFILTER_WRAM = 0x16,
GBA_SWI_DIFF_8BIT_UNFILTER_VRAM = 0x17,
GBA_SWI_DIFF_16BIT_UNFILTER = 0x18,
GBA_SWI_SOUND_BIAS = 0x19,
GBA_SWI_SOUND_DRIVER_INIT = 0x1A,
GBA_SWI_SOUND_DRIVER_MODE = 0x1B,
GBA_SWI_SOUND_DRIVER_MAIN = 0x1C,
GBA_SWI_SOUND_DRIVER_VSYNC = 0x1D,
GBA_SWI_SOUND_CHANNEL_CLEAR = 0x1E,
GBA_SWI_MIDI_KEY_2_FREQ = 0x1F,
GBA_SWI_MUSIC_PLAYER_OPEN = 0x20,
GBA_SWI_MUSIC_PLAYER_START = 0x21,
GBA_SWI_MUSIC_PLAYER_STOP = 0x22,
GBA_SWI_MUSIC_PLAYER_CONTINUE = 0x23,
GBA_SWI_MUSIC_PLAYER_FADE_OUT = 0x24,
GBA_SWI_MULTI_BOOT = 0x25,
GBA_SWI_HARD_RESET = 0x26,
GBA_SWI_CUSTOM_HALT = 0x27,
GBA_SWI_SOUND_DRIVER_VSYNC_OFF = 0x28,
GBA_SWI_SOUND_DRIVER_VSYNC_ON = 0x29,
GBA_SWI_SOUND_DRIVER_GET_JUMP_LIST = 0x2A,
};
struct ARMCore;
void GBASwi16(struct ARMCore* cpu, int immediate);
void GBASwi32(struct ARMCore* cpu, int immediate);

View File

@ -351,6 +351,10 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
}
void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) {
return mCoreGetMemoryBlockMasked(core, start, size, mCORE_MEMORY_MAPPED);
}
void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask) {
const struct mCoreMemoryBlock* blocks;
size_t nBlocks = core->listMemoryBlocks(core, &blocks);
size_t i;
@ -358,6 +362,9 @@ void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) {
if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) {
continue;
}
if (!(blocks[i].flags & mask)) {
continue;
}
if (start < blocks[i].start) {
continue;
}
@ -381,9 +388,9 @@ bool mCoreLoadELF(struct mCore* core, struct ELF* elf) {
for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) {
size_t bsize, esize;
Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i);
void* block = mCoreGetMemoryBlock(core, phdr->p_paddr, &bsize);
void* block = mCoreGetMemoryBlockMasked(core, phdr->p_paddr, &bsize, mCORE_MEMORY_WRITE | mCORE_MEMORY_WORM);
char* bytes = ELFBytes(elf, &esize);
if (block && bsize >= phdr->p_filesz && esize >= phdr->p_filesz + phdr->p_offset) {
if (block && bsize >= phdr->p_filesz && esize > phdr->p_offset && esize >= phdr->p_filesz + phdr->p_offset) {
memcpy(block, &bytes[phdr->p_offset], phdr->p_filesz);
} else {
return false;

View File

@ -65,6 +65,7 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
encoder->cycles = GBA_ARM7TDMI_FREQUENCY;
encoder->frameskip = 1;
encoder->skipResidue = 0;
encoder->loop = false;
encoder->ipixFormat =
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
@ -232,11 +233,15 @@ void FFmpegEncoderSetDimensions(struct FFmpegEncoder* encoder, int width, int he
encoder->height = height > 0 ? height : GBA_VIDEO_VERTICAL_PIXELS;
}
void FFmpegEncoderSetLooping(struct FFmpegEncoder* encoder, bool loop) {
encoder->loop = loop;
}
bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder* encoder) {
AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0);
AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
if ((encoder->audioCodec && !acodec) || (encoder->videoCodec && !vcodec) || !oformat) {
if ((encoder->audioCodec && !acodec) || (encoder->videoCodec && !vcodec) || !oformat || (!acodec && !vcodec)) {
return false;
}
if (encoder->audioCodec && !avformat_query_codec(oformat, acodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
@ -449,8 +454,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->sinkFrame = avcodec_alloc_frame();
#endif
}
if (avcodec_open2(encoder->video, vcodec, 0) < 0) {
AVDictionary* opts = 0;
av_dict_set(&opts, "strict", "-2", 0);
int res = avcodec_open2(encoder->video, vcodec, &opts);
av_dict_free(&opts);
if (res < 0) {
FFmpegEncoderClose(encoder);
return false;
}
@ -470,7 +478,17 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
#endif
}
if (avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0 || avformat_write_header(encoder->context, 0) < 0) {
if (strcmp(encoder->containerFormat, "gif") == 0) {
av_opt_set(encoder->context->priv_data, "loop", encoder->loop ? "0" : "-1", 0);
} else if (strcmp(encoder->containerFormat, "apng") == 0) {
av_opt_set(encoder->context->priv_data, "plays", encoder->loop ? "0" : "1", 0);
}
AVDictionary* opts = 0;
av_dict_set(&opts, "strict", "-2", 0);
bool res = avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0 || avformat_write_header(encoder->context, &opts) < 0;
av_dict_free(&opts);
if (res) {
FFmpegEncoderClose(encoder);
return false;
}

View File

@ -82,6 +82,7 @@ struct FFmpegEncoder {
unsigned cycles;
int frameskip;
int skipResidue;
bool loop;
int64_t currentVideoFrame;
struct SwsContext* scaleContext;
struct AVStream* videoStream;
@ -98,6 +99,7 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder*, const char* acodec, unsigned a
bool FFmpegEncoderSetVideo(struct FFmpegEncoder*, const char* vcodec, unsigned vbr, int frameskip);
bool FFmpegEncoderSetContainer(struct FFmpegEncoder*, const char* container);
void FFmpegEncoderSetDimensions(struct FFmpegEncoder*, int width, int height);
void FFmpegEncoderSetLooping(struct FFmpegEncoder*, bool loop);
bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder*);
bool FFmpegEncoderOpen(struct FFmpegEncoder*, const char* outfile);
void FFmpegEncoderClose(struct FFmpegEncoder*);

View File

@ -1078,9 +1078,13 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
LOAD_32LE(audio->ch4.lastEvent, 0, &state->ch4.lastEvent);
LOAD_32LE(when, 0, &state->ch4.nextEvent);
if (audio->ch4.envelope.dead < 2 && audio->playingCh4) {
if (when - audio->ch4.lastEvent > (uint32_t) audio->sampleInterval) {
if (!audio->ch4.lastEvent) {
// Back-compat: fake this value
audio->ch4.lastEvent = when - audio->sampleInterval;
uint32_t currentTime = mTimingCurrentTime(audio->timing);
int32_t cycles = audio->ch4.ratio ? 2 * audio->ch4.ratio : 1;
cycles <<= audio->ch4.frequency;
cycles *= 8 * audio->timingFactor;
audio->ch4.lastEvent = currentTime + (when & (cycles - 1)) - cycles;
}
mTimingSchedule(audio->timing, &audio->ch4Event, when);
}

View File

@ -45,7 +45,7 @@ static const struct mCoreChannelInfo _GBAudioChannels[] = {
static const struct mCoreMemoryBlock _GBMemoryBlocks[] = {
{ -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
{ GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
{ GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2 , GB_SIZE_WORKING_RAM_BANK0 * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
@ -56,7 +56,7 @@ static const struct mCoreMemoryBlock _GBMemoryBlocks[] = {
static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
{ -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
{ GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
{ GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
{ GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 },

View File

@ -16,13 +16,17 @@ static void _GBCLIDebuggerInit(struct CLIDebuggerSystem*);
static bool _GBCLIDebuggerCustom(struct CLIDebuggerSystem*);
static void _frame(struct CLIDebugger*, struct CLIDebugVector*);
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
static void _load(struct CLIDebugger*, struct CLIDebugVector*);
static void _save(struct CLIDebugger*, struct CLIDebugVector*);
#endif
struct CLIDebuggerCommandSummary _GBCLIDebuggerCommands[] = {
{ "frame", _frame, "", "Frame advance" },
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
{ "load", _load, "*", "Load a savestate" },
{ "save", _save, "*", "Save a savestate" },
#endif
{ 0, 0, 0, 0 }
};
@ -73,6 +77,7 @@ static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
gbDebugger->inVblank = GBRegisterSTATGetMode(((struct GB*) gbDebugger->core->board)->memory.io[REG_STAT]) == 1;
}
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct CLIDebuggerBackend* be = debugger->backend;
if (!dv || dv->type != CLIDV_INT_TYPE) {
@ -106,3 +111,4 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA);
}
#endif

View File

@ -1,4 +1,5 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
/* Copyright (c) 2013-2019 Jeffrey Pfau
>>>>>>> master
*
* 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
@ -17,7 +18,7 @@ const struct mInputPlatformInfo GBInputInfo = {
"Right",
"Left",
"Up",
"Down"
"Down",
},
.nKeys = GB_KEY_MAX,
.hat = {

View File

@ -104,6 +104,8 @@ void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
}
void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info) {
info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED);
info->reg = GBADMARegisterSetWidth(info->reg, 1);
switch (info->dest) {
case BASE_IO | REG_FIFO_A_LO:
audio->chA.dmaSource = number;
@ -129,8 +131,6 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
audio->externalMixing = false;
}
}
info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED);
info->reg = GBADMARegisterSetWidth(info->reg, 1);
}
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {

View File

@ -424,43 +424,43 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
return;
}
switch (immediate) {
case 0x0:
case GBA_SWI_SOFT_RESET:
_SoftReset(gba);
break;
case 0x1:
case GBA_SWI_REGISTER_RAM_RESET:
_RegisterRamReset(gba);
break;
case 0x2:
GBAHalt(gba);
break;
case 0x3:
case GBA_SWI_HALT:
ARMRaiseSWI(cpu);
return;
case GBA_SWI_STOP:
GBAStop(gba);
break;
case 0x05:
case GBA_SWI_VBLANK_INTR_WAIT:
// VBlankIntrWait
// Fall through:
case 0x04:
case GBA_SWI_INTR_WAIT:
// IntrWait
ARMRaiseSWI(cpu);
break;
case 0x6:
return;
case GBA_SWI_DIV:
_Div(gba, cpu->gprs[0], cpu->gprs[1]);
break;
case 0x7:
case GBA_SWI_DIV_ARM:
_Div(gba, cpu->gprs[1], cpu->gprs[0]);
break;
case 0x8:
case GBA_SWI_SQRT:
cpu->gprs[0] = _Sqrt(cpu->gprs[0], &cpu->cycles);
break;
case 0x9:
case GBA_SWI_ARCTAN:
cpu->gprs[0] = _ArcTan(cpu->gprs[0], &cpu->gprs[1], &cpu->gprs[3], &cpu->cycles);
break;
case 0xA:
case GBA_SWI_ARCTAN2:
cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1], &cpu->gprs[1], &cpu->cycles);
cpu->gprs[3] = 0x170;
break;
case 0xB:
case 0xC:
case GBA_SWI_CPU_SET:
case GBA_SWI_CPU_FAST_SET:
if (cpu->gprs[0] >> BASE_OFFSET < REGION_WORKING_RAM) {
mLOG(GBA_BIOS, GAME_ERROR, "Cannot CpuSet from BIOS");
break;
@ -472,19 +472,19 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
mLOG(GBA_BIOS, GAME_ERROR, "Misaligned CpuSet destination");
}
ARMRaiseSWI(cpu);
break;
case 0xD:
return;
case GBA_SWI_GET_BIOS_CHECKSUM:
cpu->gprs[0] = GBA_BIOS_CHECKSUM;
cpu->gprs[1] = 1;
cpu->gprs[3] = SIZE_BIOS;
break;
case 0xE:
case GBA_SWI_BG_AFFINE_SET:
_BgAffineSet(gba);
break;
case 0xF:
case GBA_SWI_OBJ_AFFINE_SET:
_ObjAffineSet(gba);
break;
case 0x10:
case GBA_SWI_BIT_UNPACK:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack source");
break;
@ -500,8 +500,8 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
break;
}
break;
case 0x11:
case 0x12:
case GBA_SWI_LZ77_UNCOMP_WRAM:
case GBA_SWI_LZ77_UNCOMP_VRAM:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
mLOG(GBA_BIOS, GAME_ERROR, "Bad LZ77 source");
break;
@ -513,11 +513,11 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
case REGION_WORKING_RAM:
case REGION_WORKING_IRAM:
case REGION_VRAM:
_unLz77(gba, immediate == 0x11 ? 1 : 2);
_unLz77(gba, immediate == GBA_SWI_LZ77_UNCOMP_WRAM ? 1 : 2);
break;
}
break;
case 0x13:
case GBA_SWI_HUFFMAN_UNCOMP:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
mLOG(GBA_BIOS, GAME_ERROR, "Bad Huffman source");
break;
@ -533,8 +533,8 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
break;
}
break;
case 0x14:
case 0x15:
case GBA_SWI_RL_UNCOMP_WRAM:
case GBA_SWI_RL_UNCOMP_VRAM:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
mLOG(GBA_BIOS, GAME_ERROR, "Bad RL source");
break;
@ -546,13 +546,13 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
case REGION_WORKING_RAM:
case REGION_WORKING_IRAM:
case REGION_VRAM:
_unRl(gba, immediate == 0x14 ? 1 : 2);
_unRl(gba, immediate == GBA_SWI_RL_UNCOMP_WRAM ? 1 : 2);
break;
}
break;
case 0x16:
case 0x17:
case 0x18:
case GBA_SWI_DIFF_8BIT_UNFILTER_WRAM:
case GBA_SWI_DIFF_8BIT_UNFILTER_VRAM:
case GBA_SWI_DIFF_16BIT_UNFILTER:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
mLOG(GBA_BIOS, GAME_ERROR, "Bad UnFilter source");
break;
@ -564,17 +564,20 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
case REGION_WORKING_RAM:
case REGION_WORKING_IRAM:
case REGION_VRAM:
_unFilter(gba, immediate == 0x18 ? 2 : 1, immediate == 0x16 ? 1 : 2);
_unFilter(gba, immediate == GBA_SWI_DIFF_16BIT_UNFILTER ? 2 : 1, immediate == GBA_SWI_DIFF_8BIT_UNFILTER_WRAM ? 1 : 2);
break;
}
break;
case 0x19:
case GBA_SWI_SOUND_BIAS:
// SoundBias is mostly meaningless here
mLOG(GBA_BIOS, STUB, "Stub software interrupt: SoundBias (19)");
break;
case 0x1F:
case GBA_SWI_MIDI_KEY_2_FREQ:
_MidiKey2Freq(gba);
break;
case GBA_SWI_SOUND_DRIVER_GET_JUMP_LIST:
ARMRaiseSWI(cpu);
return;
default:
mLOG(GBA_BIOS, STUB, "Stub software interrupt: %02X", immediate);
}

View File

@ -62,9 +62,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocks[] = {
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
};
static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = {
@ -76,9 +76,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = {
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true },
};
@ -91,9 +91,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = {
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
};
@ -106,9 +106,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = {
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
};
@ -121,9 +121,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
{ REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW },
};
@ -647,7 +647,7 @@ static void _GBACoreReset(struct mCore* core) {
#endif
ARMReset(core->cpu);
if (core->opts.skipBios && (gba->romVf || gba->memory.rom)) {
if ((core->opts.skipBios && (gba->romVf || gba->memory.rom)) || (gba->romVf && GBAIsMB(gba->romVf))) {
GBASkipBIOS(core->board);
}
}

View File

@ -16,13 +16,17 @@ static void _GBACLIDebuggerInit(struct CLIDebuggerSystem*);
static bool _GBACLIDebuggerCustom(struct CLIDebuggerSystem*);
static void _frame(struct CLIDebugger*, struct CLIDebugVector*);
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
static void _load(struct CLIDebugger*, struct CLIDebugVector*);
static void _save(struct CLIDebugger*, struct CLIDebugVector*);
#endif
struct CLIDebuggerCommandSummary _GBACLIDebuggerCommands[] = {
{ "frame", _frame, "", "Frame advance" },
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
{ "load", _load, "*", "Load a savestate" },
{ "save", _save, "*", "Save a savestate" },
#endif
{ 0, 0, 0, 0 }
};
@ -72,6 +76,7 @@ static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(((struct GBA*) gbaDebugger->core->board)->memory.io[REG_DISPSTAT >> 1]);
}
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct CLIDebuggerBackend* be = debugger->backend;
if (!dv || dv->type != CLIDV_INT_TYPE) {
@ -107,3 +112,4 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA);
}
#endif

View File

@ -253,7 +253,7 @@ void GBASkipBIOS(struct GBA* gba) {
if (gba->memory.rom) {
cpu->gprs[ARM_PC] = BASE_CART0;
} else {
cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0;
}
gba->video.vcount = 0x7D;
gba->memory.io[REG_VCOUNT >> 1] = 0x7D;
@ -741,11 +741,9 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) {
.type.bp.opcode = opcode
};
mDebuggerEnter(gba->debugger->d.p, DEBUGGER_ENTER_ILLEGAL_OP, &info);
} else
#endif
{
ARMRaiseUndefined(cpu);
}
#endif
ARMRaiseUndefined(cpu);
}
void GBABreakpoint(struct ARMCore* cpu, int immediate) {

View File

@ -3,51 +3,74 @@
#include <mgba/internal/gba/memory.h>
const uint8_t hleBios[SIZE_BIOS] = {
0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x0b, 0x00, 0x00, 0xea,
0x06, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0b, 0x00, 0x00, 0xea,
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
0x2c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3,
0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0xf8, 0x01, 0x9f, 0x15,
0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3,
0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0x4c, 0x01, 0x9f, 0x15,
0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1,
0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02,
0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x9c, 0xc0, 0xa0, 0xe3,
0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0xd4, 0xc0, 0xa0, 0xe3,
0x0b, 0xb1, 0x9c, 0xe7, 0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1,
0x00, 0x10, 0x2d, 0xe9, 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3,
0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1,
0x1b, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3,
0x00, 0x10, 0xbd, 0xe8, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8,
0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00,
0xd4, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3,
0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x00, 0xa0, 0xe1,
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
0x0f, 0xe0, 0xa0, 0xe1, 0x1b, 0xff, 0x2f, 0x11, 0x00, 0x00, 0xa0, 0xe1,
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x10, 0xbd, 0xe8,
0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1,
0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x02, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3,
0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5,
0x01, 0x00, 0xa0, 0xe3, 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9,
0x01, 0xc3, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3,
0x01, 0x20, 0xa0, 0xe3, 0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1,
0x01, 0x30, 0xc3, 0xe1, 0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5,
0x08, 0x02, 0xcc, 0xe5, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0,
0x01, 0x30, 0x23, 0x10, 0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5,
0xf7, 0xff, 0xff, 0x0a, 0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9,
0x02, 0x46, 0xa0, 0xe1, 0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1,
0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3,
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8,
0x04, 0x00, 0x55, 0xe1, 0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba,
0x14, 0x00, 0x00, 0xea, 0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3,
0xa4, 0x45, 0x85, 0xe0, 0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1,
0xb2, 0x30, 0xc5, 0xb0, 0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea,
0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0,
0x04, 0x00, 0x55, 0xe1, 0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8,
0xfb, 0xff, 0xff, 0xba, 0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0,
0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0,
0xfb, 0xff, 0xff, 0xba, 0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8,
0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1,
0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5,
0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1,
0x03, 0x70, 0xa0, 0xe1, 0x03, 0x80, 0xa0, 0xe1, 0x03, 0x90, 0xa0, 0xe1,
0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8,
0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1,
0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
0xf0, 0x87, 0xbd, 0xe8, 0xc0, 0x00, 0x00, 0x02
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03,
0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x20, 0xa0, 0xe3, 0x01, 0xc3, 0xa0, 0xe3,
0x01, 0x23, 0xcc, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x00, 0xa0, 0xe3,
0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3,
0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3,
0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0xc3, 0xe1,
0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5,
0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10,
0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a,
0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x46, 0xa0, 0xe1,
0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3,
0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a,
0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8, 0x04, 0x00, 0x55, 0xe1,
0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea,
0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, 0xa4, 0x45, 0x85, 0xe0,
0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xc5, 0xb0,
0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3,
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8, 0xfb, 0xff, 0xff, 0xba,
0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, 0xfb, 0xff, 0xff, 0xba,
0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9,
0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0,
0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1,
0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1,
0x03, 0x80, 0xa0, 0xe1, 0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1,
0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba,
0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8,
0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, 0xf0, 0x87, 0xbd, 0xe8,
0xf0, 0x07, 0x2d, 0xe9, 0x38, 0x10, 0x9f, 0xe5, 0x01, 0x30, 0xa0, 0xe1,
0x01, 0x40, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x60, 0xa0, 0xe1,
0x01, 0x70, 0xa0, 0xe1, 0x01, 0x80, 0xa0, 0xe1, 0x01, 0x90, 0xa0, 0xe1,
0x01, 0xa0, 0xa0, 0xe1, 0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8,
0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0x00, 0x10, 0xa0, 0xe3,
0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00
};

View File

@ -40,8 +40,22 @@ and r12, #0x80
orr r12, #0x1F
msr cpsr, r12
stmfd sp!, {lr}
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
mov lr, pc
bxne r11
nop
nop
nop
nop
ldmfd sp!, {lr}
msr cpsr, #0x93
ldmfd sp!, {r12}
@ -52,20 +66,51 @@ movs pc, lr
.word 0xE3A02004
swiTable:
.word SoftReset
.word RegisterRamReset
.word Halt
.word Stop
.word IntrWait
.word VBlankIntrWait
.word Div
.word DivArm
.word Sqrt
.word ArcTan
.word ArcTan2
.word CpuSet
.word CpuFastSet
@ ... The rest of this table isn't needed if the rest aren't implemented
.word SoftReset @ 0x00
.word RegisterRamReset @ 0x01
.word Halt @ 0x02
.word Stop @ 0x03
.word IntrWait @ 0x04
.word VBlankIntrWait @ 0x05
.word Div @ 0x06
.word DivArm @ 0x07
.word Sqrt @ 0x08
.word ArcTan @ 0x09
.word ArcTan2 @ 0x0A
.word CpuSet @ 0x0B
.word CpuFastSet @ 0x0C
.word GetBiosChecksum @ 0x0D
.word BgAffineSet @ 0x0E
.word ObjAffineSet @ 0x0F
.word BitUnPack @ 0x10
.word Lz77UnCompWram @ 0x11
.word Lz77UnCompVram @ 0x12
.word HuffmanUnComp @ 0x13
.word RlUnCompWram @ 0x14
.word RlUnCompVram @ 0x15
.word Diff8BitUnFilterWram @ 0x16
.word Diff8BitUnFilterVram @ 0x17
.word Diff16BitUnFilter @ 0x18
.word SoundBias @ 0x19
.word SoundDriverInit @ 0x1A
.word SoundDriverMode @ 0x1B
.word SoundDriverMain @ 0x1C
.word SoundDriverVsync @ 0x1D
.word SoundChannelClear @ 0x1E
.word MidiKey2Freq @ 0x1F
.word MusicPlayerOpen @ 0x20
.word MusicPlayerStart @ 0x21
.word MusicPlayerStop @ 0x22
.word MusicPlayerContinue @ 0x23
.word MusicPlayerFadeOut @ 0x24
.word MultiBoot @ 0x25
.word HardReset @ 0x26
.word CustomHalt @ 0x27
.word SoundDriverVsyncOff @ 0x28
.word SoundDriverVsyncOn @ 0x29
.word SoundDriverGetJumpList @ 0x2A
.ltorg
irqBase:
stmfd sp!, {r0-r3, r12, lr}
@ -77,6 +122,59 @@ subs pc, lr, #4
.word 0
.word 0xE55EC002
undefBase:
subs pc, lr, #4
.word 0
.word 0x03A0E004
@ Unimplemented
SoftReset:
RegisterRamReset:
Stop:
Div:
DivArm:
Sqrt:
ArcTan:
ArcTan2:
GetBiosChecksum:
BgAffineSet:
ObjAffineSet:
BitUnPack:
Lz77UnCompWram:
Lz77UnCompVram:
HuffmanUnComp:
RlUnCompWram:
RlUnCompVram:
Diff8BitUnFilterWram:
Diff8BitUnFilterVram:
Diff16BitUnFilter:
SoundBias:
SoundDriverInit:
SoundDriverMode:
SoundDriverMain:
SoundDriverVsync:
SoundChannelClear:
MidiKey2Freq:
MusicPlayerOpen:
MusicPlayerStart:
MusicPlayerStop:
MusicPlayerContinue:
MusicPlayerFadeOut:
MultiBoot:
HardReset:
CustomHalt:
SoundDriverVsyncOff:
SoundDriverVsyncOn:
NopCall:
bx lr
Halt:
mov r2, #0
mov r12, #0x04000000
strb r2, [r12, #0x301]
bx lr
VBlankIntrWait:
mov r0, #1
mov r1, #1
@ -187,4 +285,23 @@ blt 0b
2:
ldmfd sp!, {r4-r10, pc}
SoundDriverGetJumpList:
stmfd sp!, {r4-r10}
ldr r1, =NopCall
mov r3, r1
mov r4, r1
mov r5, r1
mov r6, r1
mov r7, r1
mov r8, r1
mov r9, r1
mov r10, r1
stmia r0!, {r1, r3-r10}
stmia r0!, {r1, r3-r10}
stmia r0!, {r1, r3-r10}
stmia r0!, {r1, r3-r10}
mov r1, #0
ldmfd sp!, {r4-r10}
bx lr
.ltorg

View File

@ -369,6 +369,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
} else {
video->event.callback = _startHblank;
}
break;
case 1:
video->event.callback = _startHdraw;
break;

View File

@ -25,10 +25,11 @@ GIFView::GIFView(QWidget* parent)
connect(m_ui.selectFile, &QAbstractButton::clicked, this, &GIFView::selectFile);
connect(m_ui.filename, &QLineEdit::textChanged, this, &GIFView::setFilename);
connect(m_ui.fmtGif, &QAbstractButton::clicked, this, &GIFView::changeExtension);
connect(m_ui.fmtApng, &QAbstractButton::clicked, this, &GIFView::changeExtension);
FFmpegEncoderInit(&m_encoder);
FFmpegEncoderSetAudio(&m_encoder, nullptr, 0);
FFmpegEncoderSetContainer(&m_encoder, "gif");
}
GIFView::~GIFView() {
@ -44,14 +45,24 @@ void GIFView::setController(std::shared_ptr<CoreController> controller) {
}
void GIFView::startRecording() {
FFmpegEncoderSetVideo(&m_encoder, "gif", 0, m_ui.frameskip->value());
if (m_ui.fmtApng->isChecked()) {
FFmpegEncoderSetContainer(&m_encoder, "apng");
FFmpegEncoderSetVideo(&m_encoder, "apng", 0, m_ui.frameskip->value());
} else {
FFmpegEncoderSetContainer(&m_encoder, "gif");
FFmpegEncoderSetVideo(&m_encoder, "gif", 0, m_ui.frameskip->value());
}
FFmpegEncoderSetLooping(&m_encoder, m_ui.loop->isChecked());
if (!FFmpegEncoderOpen(&m_encoder, m_filename.toUtf8().constData())) {
LOG(QT, ERROR) << tr("Failed to open output GIF file: %1").arg(m_filename);
LOG(QT, ERROR) << tr("Failed to open output GIF or APNG file: %1").arg(m_filename);
return;
}
m_ui.start->setEnabled(false);
m_ui.stop->setEnabled(true);
m_ui.frameskip->setEnabled(false);
m_ui.loop->setEnabled(false);
m_ui.fmtApng->setEnabled(false);
m_ui.fmtGif->setEnabled(false);
emit recordingStarted(&m_encoder.d);
}
@ -59,22 +70,45 @@ void GIFView::stopRecording() {
emit recordingStopped();
FFmpegEncoderClose(&m_encoder);
m_ui.stop->setEnabled(false);
m_ui.start->setEnabled(true);
m_ui.start->setEnabled(!m_filename.isEmpty());
m_ui.frameskip->setEnabled(true);
m_ui.loop->setEnabled(true);
m_ui.fmtApng->setEnabled(true);
m_ui.fmtGif->setEnabled(true);
}
void GIFView::selectFile() {
QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"), tr("Graphics Interchange Format (*.gif)"));
if (!filename.isEmpty()) {
m_ui.filename->setText(filename);
if (!FFmpegEncoderIsOpen(&m_encoder)) {
m_ui.start->setEnabled(true);
QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"), tr("Graphics Interchange Format (*.gif);;Animated Portable Network Graphics (*.png *.apng)"));
m_ui.filename->setText(filename);
}
void GIFView::setFilename(const QString& filename) {
m_filename = filename;
if (!FFmpegEncoderIsOpen(&m_encoder)) {
m_ui.start->setEnabled(!filename.isEmpty());
if (filename.endsWith(".gif")) {
m_ui.fmtGif->setChecked(Qt::Checked);
} else if (filename.endsWith(".png") || filename.endsWith(".apng")) {
m_ui.fmtApng->setChecked(Qt::Checked);
}
}
}
void GIFView::setFilename(const QString& fname) {
m_filename = fname;
void GIFView::changeExtension() {
if (m_filename.isEmpty()) {
return;
}
QString filename = m_filename;
int index = m_filename.lastIndexOf(".");
if (index >= 0) {
filename.truncate(index);
}
if (m_ui.fmtGif->isChecked()) {
filename += ".gif";
} else if (m_ui.fmtApng->isChecked()) {
filename += ".png";
}
m_ui.filename->setText(filename);
}
#endif

View File

@ -41,6 +41,7 @@ signals:
private slots:
void selectFile();
void setFilename(const QString&);
void changeExtension();
private:
Ui::GIFView m_ui;

View File

@ -7,50 +7,42 @@
<x>0</x>
<y>0</y>
<width>392</width>
<height>220</height>
<height>262</height>
</rect>
</property>
<property name="windowTitle">
<string>Record GIF</string>
<string>Record GIF/APNG</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="1" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label">
<item row="2" column="0">
<widget class="QRadioButton" name="fmtApng">
<property name="text">
<string>Frameskip</string>
<string>APNG</string>
</property>
<attribute name="buttonGroup">
<string notr="true">format</string>
</attribute>
</widget>
</item>
<item row="1" column="2">
<item row="2" column="2">
<widget class="QSpinBox" name="frameskip">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>9</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
@ -123,8 +115,55 @@
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Frameskip</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="fmtGif">
<property name="text">
<string>GIF</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">format</string>
</attribute>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QCheckBox" name="loop">
<property name="text">
<string>Loop</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>filename</tabstop>
<tabstop>start</tabstop>
<tabstop>stop</tabstop>
<tabstop>selectFile</tabstop>
<tabstop>fmtGif</tabstop>
<tabstop>fmtApng</tabstop>
<tabstop>loop</tabstop>
<tabstop>frameskip</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
@ -144,4 +183,7 @@
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="format"/>
</buttongroups>
</ui>

View File

@ -9,6 +9,7 @@
#include "GamepadButtonEvent.h"
#include "InputIndex.h"
#include <QCoreApplication>
#include <QFontMetrics>
#include <QKeyEvent>
@ -32,7 +33,35 @@ void KeyEditor::setValue(int key) {
if (key < 0) {
setText(tr("---"));
} else {
setText(QKeySequence(key).toString(QKeySequence::NativeText));
QKeySequence seq(key);
switch (key) {
#ifndef Q_OS_MAC
case Qt::Key_Shift:
setText(QCoreApplication::translate("QShortcut", "Shift"));
break;
case Qt::Key_Control:
setText(QCoreApplication::translate("QShortcut", "Control"));
break;
case Qt::Key_Alt:
setText(QCoreApplication::translate("QShortcut", "Alt"));
break;
case Qt::Key_Meta:
setText(QCoreApplication::translate("QShortcut", "Meta"));
break;
#endif
case Qt::Key_Super_L:
setText(tr("Super (L)"));
break;
case Qt::Key_Super_R:
setText(tr("Super (R)"));
break;
case Qt::Key_Menu:
setText(tr("Menu"));
break;
default:
setText(QKeySequence(key).toString(QKeySequence::NativeText));
break;
}
}
}
emit valueChanged(key);

View File

@ -16,11 +16,13 @@
using namespace QGBA;
#ifdef M_CORE_GB
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
: controller(coreController)
, gbNode(node)
{
}
#endif
#ifdef M_CORE_GBA
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)

View File

@ -44,6 +44,10 @@ ObjView::ObjView(std::shared_ptr<CoreController> controller, QWidget* parent)
m_ui.priority->setFont(font);
m_ui.palette->setFont(font);
m_ui.transform->setFont(font);
m_ui.xformPA->setFont(font);
m_ui.xformPB->setFont(font);
m_ui.xformPC->setFont(font);
m_ui.xformPD->setFont(font);
m_ui.mode->setFont(font);
connect(m_ui.tiles, &TilePainter::indexPressed, this, &ObjView::translateIndex);
@ -157,9 +161,23 @@ void ObjView::updateTilesGBA(bool force) {
m_ui.mosaic->setChecked(GBAObjAttributesAIsMosaic(obj->a));
if (GBAObjAttributesAIsTransformed(obj->a)) {
m_ui.transform->setText(QString::number(GBAObjAttributesBGetMatIndex(obj->b)));
int mtxId = GBAObjAttributesBGetMatIndex(obj->b);
struct GBAOAMMatrix mat;
LOAD_16LE(mat.a, 0, &gba->video.oam.mat[mtxId].a);
LOAD_16LE(mat.b, 0, &gba->video.oam.mat[mtxId].b);
LOAD_16LE(mat.c, 0, &gba->video.oam.mat[mtxId].c);
LOAD_16LE(mat.d, 0, &gba->video.oam.mat[mtxId].d);
m_ui.transform->setText(QString::number(mtxId));
m_ui.xformPA->setText(QString("%0").arg(mat.a / 256., 5, 'f', 2));
m_ui.xformPB->setText(QString("%0").arg(mat.b / 256., 5, 'f', 2));
m_ui.xformPC->setText(QString("%0").arg(mat.c / 256., 5, 'f', 2));
m_ui.xformPD->setText(QString("%0").arg(mat.d / 256., 5, 'f', 2));
} else {
m_ui.transform->setText(tr("Off"));
m_ui.xformPA->setText(tr("---"));
m_ui.xformPB->setText(tr("---"));
m_ui.xformPC->setText(tr("---"));
m_ui.xformPD->setText(tr("---"));
}
switch (GBAObjAttributesAGetMode(obj->a)) {
@ -230,6 +248,10 @@ void ObjView::updateTilesGB(bool force) {
m_ui.doubleSize->setChecked(false);
m_ui.mosaic->setChecked(false);
m_ui.transform->setText(tr("N/A"));
m_ui.xformPA->setText(tr("---"));
m_ui.xformPB->setText(tr("---"));
m_ui.xformPC->setText(tr("---"));
m_ui.xformPD->setText(tr("---"));
m_ui.mode->setText(tr("N/A"));
}
#endif

View File

@ -13,7 +13,81 @@
<property name="windowTitle">
<string>Sprites</string>
</property>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,1" columnstretch="0,0,1,1">
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0" columnstretch="0,0,0,1">
<item row="0" column="2" rowspan="4" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QGBA::TilePainter" name="tiles" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>8</width>
<height>8</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="objId">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>127</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Address</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="address">
<property name="text">
<string>0x07000000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="copyButton">
<property name="text">
@ -21,13 +95,49 @@
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSpinBox" name="magnification">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Magnification</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Geometry</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
@ -93,7 +203,14 @@
</item>
</layout>
</item>
<item>
<item row="0" column="1" rowspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_4">
@ -159,50 +276,86 @@
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="2" rowspan="4" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QGBA::TilePainter" name="tiles" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>8</width>
<height>8</height>
</size>
</property>
</widget>
<item row="0" column="2" rowspan="2">
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="3">
<widget class="QLabel" name="xformPC">
<property name="text">
<string>+0.00</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="xformPA">
<property name="text">
<string>+1.00</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Matrix</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="xformPD">
<property name="text">
<string>+1.00</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QLabel" name="xformPB">
<property name="text">
<string>+0.00</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" rowspan="5">
<widget class="QGBA::AssetTile" name="tile">
<property name="title">
<string>Tile</string>
<item row="5" column="0" colspan="4">
<widget class="QListWidget" name="objList">
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="flow">
<enum>QListView::LeftToRight</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="gridSize">
<size>
<width>64</width>
<height>96</height>
</size>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
@ -215,6 +368,12 @@
</item>
<item row="3" column="0" rowspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Attributes</string>
</property>
@ -517,98 +676,10 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="objId">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>127</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Address</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="address">
<property name="text">
<string>0x07000000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSpinBox" name="magnification">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Magnification</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="4">
<widget class="QListWidget" name="objList">
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="flow">
<enum>QListView::LeftToRight</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="gridSize">
<size>
<width>64</width>
<height>96</height>
</size>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
<item row="0" column="1" rowspan="5">
<widget class="QGBA::AssetTile" name="tile">
<property name="title">
<string>Tile</string>
</property>
</widget>
</item>

View File

@ -120,7 +120,7 @@ void PaletteView::selectIndex(int index) {
hexcode |= (hexcode >> 5) & 0x070707;
m_ui.hexcode->setText(tr("#%0").arg(hexcode, 6, 16, QChar('0')));
m_ui.value->setText(tr("0x%0").arg(color, 4, 16, QChar('0')));
m_ui.index->setText(tr("%0").arg(index, 3, 10, QChar('0')));
m_ui.index->setText(tr("0x%0 (%1)").arg(index, 3, 16, QChar('0')).arg(index, 3, 10, QChar('0')));
m_ui.r->setText(tr("0x%0 (%1)").arg(r, 2, 16, QChar('0')).arg(r, 2, 10, QChar('0')));
m_ui.g->setText(tr("0x%0 (%1)").arg(g, 2, 16, QChar('0')).arg(g, 2, 10, QChar('0')));
m_ui.b->setText(tr("0x%0 (%1)").arg(b, 2, 16, QChar('0')).arg(b, 2, 10, QChar('0')));

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>443</width>
<width>500</width>
<height>397</height>
</rect>
</property>
@ -289,7 +289,7 @@
<item>
<widget class="QLabel" name="index">
<property name="text">
<string>000</string>
<string>0x000 (000)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>

View File

@ -112,6 +112,15 @@ VideoView::VideoView(QWidget* parent)
void VideoView::updatePresets() {
m_presets.clear();
addPreset(m_ui.preset4K, {
.container = QString(),
.vcodec = QString(),
.acodec = QString(),
.vbr = 0,
.abr = 0,
.dims = maintainAspect(QSize(3840, 2160))
});
addPreset(m_ui.preset1080, {
.container = QString(),
.vcodec = QString(),
@ -177,6 +186,14 @@ void VideoView::updatePresets() {
.abr = 128
});
addPreset(m_ui.presetMP4, {
.container = "MP4",
.vcodec = "h.264",
.acodec = "AAC",
.vbr = 800,
.abr = 128
});
if (m_nativeWidth && m_nativeHeight) {
addPreset(m_ui.presetLossless, {
.container = "MKV",

View File

@ -133,6 +133,13 @@
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="presetMP4">
<property name="text">
<string>MP4</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="presetLossless">
<property name="text">
@ -150,6 +157,13 @@
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QRadioButton" name="preset4K">
<property name="text">
<string>4K</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="preset1080">
<property name="text">
@ -289,6 +303,11 @@
<string>FFV1</string>
</property>
</item>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item>
@ -326,6 +345,11 @@
<string>Uncompressed</string>
</property>
</item>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
</layout>

View File

@ -871,7 +871,6 @@ void Window::gameStopped() {
action->setEnabled(false);
}
setWindowFilePath(QString());
updateTitle();
detachWidget(m_display.get());
m_screenWidget->setCenteredAspectRatio(m_logo.width(), m_logo.height());
m_screenWidget->setDimensions(m_logo.width(), m_logo.height());
@ -900,6 +899,7 @@ void Window::gameStopped() {
m_display->stopDrawing();
m_controller.reset();
updateTitle();
m_display->setVideoProxy({});
if (m_pendingClose) {
@ -1015,6 +1015,8 @@ void Window::reloadAudioDriver() {
m_audioProcessor->start();
connect(m_controller.get(), &CoreController::stopping, m_audioProcessor.get(), &AudioProcessor::stop);
connect(m_controller.get(), &CoreController::fastForwardChanged, m_audioProcessor.get(), &AudioProcessor::inputParametersChanged);
connect(m_controller.get(), &CoreController::paused, m_audioProcessor.get(), &AudioProcessor::pause);
connect(m_controller.get(), &CoreController::unpaused, m_audioProcessor.get(), &AudioProcessor::start);
}
void Window::changeRenderer() {
@ -1547,7 +1549,7 @@ void Window::setupMenu(QMenuBar* menubar) {
#ifdef USE_FFMPEG
addGameAction(tr("Record A/V..."), "recordOutput", this, &Window::openVideoWindow, "av");
addGameAction(tr("Record GIF..."), "recordGIF", this, &Window::openGIFWindow, "av");
addGameAction(tr("Record GIF/APNG..."), "recordGIF", this, &Window::openGIFWindow, "av");
#endif
m_actions.addSeparator("av");

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,7 @@ if(USE_PIXMAN)
endif()
if(WIN32)
list(APPEND SDL_LIBRARY imm32 version winmm)
list(APPEND SDL_LIBRARY imm32 setupapi version winmm)
elseif(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AppKit -framework AudioUnit -framework Carbon -framework CoreAudio -framework AudioToolbox -framework ForceFeedback -framework IOKit")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" PARENT_SCOPE)

View File

@ -19,6 +19,11 @@ mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio", "platform.sdl.audio");
static void _mSDLAudioCallback(void* context, Uint8* data, int len);
bool mSDLInitAudio(struct mSDLAudio* context, struct mCoreThread* threadContext) {
#if defined(_WIN32) && SDL_VERSION_ATLEAST(2, 0, 8)
if (!getenv("SDL_AUDIODRIVER")) {
_putenv_s("SDL_AUDIODRIVER", "directsound");
}
#endif
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
mLOG(SDL_AUDIO, ERROR, "Could not initialize SDL sound system: %s", SDL_GetError());
return false;

View File

@ -348,6 +348,13 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration*
if (!sdlJoystick) {
continue;
}
ssize_t joysticks[MAX_PLAYERS];
size_t i;
// Pointers can get invalidated, so we'll need to refresh them
for (i = 0; i < events->playersAttached && i < MAX_PLAYERS; ++i) {
joysticks[i] = events->players[i]->joystick ? SDL_JoystickListIndex(&events->joysticks, events->players[i]->joystick) : SIZE_MAX;
events->players[i]->joystick = NULL;
}
struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&events->joysticks);
joystick->joystick = sdlJoystick;
joystick->id = SDL_JoystickInstanceID(joystick->joystick);
@ -355,8 +362,12 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration*
#if SDL_VERSION_ATLEAST(2, 0, 0)
joystick->haptic = SDL_HapticOpenFromJoystick(joystick->joystick);
#endif
for (i = 0; i < events->playersAttached && i < MAX_PLAYERS; ++i) {
if (joysticks[i] != SIZE_MAX) {
events->players[i]->joystick = SDL_JoystickListGetPointer(&events->joysticks, joysticks[i]);
}
}
size_t i;
#if SDL_VERSION_ATLEAST(2, 0, 0)
char joystickName[34] = {0};
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick->joystick), joystickName, sizeof(joystickName));

View File

@ -22,6 +22,14 @@
#ifdef __SWITCH__
#include <switch.h>
#endif
#ifdef GEKKO
#include <fat.h>
#include <gccore.h>
#ifdef FIXED_ROM_BUFFER
uint32_t* romBuffer;
size_t romBufferSize;
#endif
#endif
#include <errno.h>
#include <fcntl.h>
@ -83,6 +91,29 @@ int main(int argc, char** argv) {
#elif defined(__SWITCH__)
UNUSED(_mPerfShutdown);
consoleInit(NULL);
#elif defined(GEKKO)
VIDEO_Init();
VIDEO_SetBlack(true);
VIDEO_Flush();
VIDEO_WaitVSync();
GXRModeObj* vmode = VIDEO_GetPreferredMode(0);
void* xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(vmode));
console_init(xfb, 20, 20, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * VI_DISPLAY_PIX_SZ);
VIDEO_Configure(vmode);
VIDEO_SetNextFramebuffer(xfb);
VIDEO_SetBlack(false);
VIDEO_Flush();
VIDEO_WaitVSync();
VIDEO_WaitVSync();
fatInitDefault();
#ifdef FIXED_ROM_BUFFER
romBufferSize = 0x02000000;
romBuffer = SYS_GetArena2Lo();
SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize));
#endif
#else
signal(SIGINT, _mPerfShutdown);
#endif
@ -125,6 +156,8 @@ int main(int argc, char** argv) {
puts("game_code,frames,duration,renderer");
#ifdef __SWITCH__
consoleUpdate(NULL);
#elif defined(GEKKO)
VIDEO_WaitVSync();
#endif
}
if (perfOpts.server) {
@ -145,6 +178,11 @@ int main(int argc, char** argv) {
acExit();
#elif defined(__SWITCH__)
consoleExit(NULL);
#elif defined(GEKKO)
VIDEO_SetBlack(true);
VIDEO_Flush();
VIDEO_WaitVSync();
VIDEO_WaitVSync();
#endif
return didFail;

View File

@ -11,6 +11,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/wii-*.c)
list(APPEND OS_LIB wiiuse bte fat ogc)
set(OS_SRC ${OS_SRC} PARENT_SCOPE)
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
source_group("Wii-specific code" FILES ${OS_SRC})
set(CORE_VFS_SRC ${CORE_VFS_SRC} PARENT_SCOPE)
set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)
@ -39,6 +40,16 @@ add_custom_target(${BINARY_NAME}.dol ALL
add_custom_target(run ${WIILOAD} ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.dol
DEPENDS ${BINARY_NAME}.dol)
if(BUILD_PERF)
add_custom_target(${BINARY_NAME}-perf.dol ALL
${ELF2DOL} ../${BINARY_NAME}-perf ${BINARY_NAME}-perf.dol
DEPENDS ${BINARY_NAME}-perf)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}-perf.dol
DESTINATION . COMPONENT ${BINARY_NAME}-perf)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/meta.xml.in ${CMAKE_CURRENT_BINARY_DIR}/meta.xml)
install(TARGETS ${BINARY_NAME}.elf DESTINATION . COMPONENT ${BINARY_NAME}-dbg)

View File

@ -262,7 +262,7 @@ int main(int argc, char* argv[]) {
#ifdef FIXED_ROM_BUFFER
romBufferSize = SIZE_CART0;
romBuffer = SYS_GetArena2Lo();
SYS_SetArena2Lo((void*)((intptr_t) romBuffer + SIZE_CART0));
SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize));
#endif
#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2020 Jeffrey Pfau
*
* 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
@ -14,11 +14,23 @@
#include <windows.h>
#endif
#include <mgba-util/vector.h>
#ifdef _WIN32
struct HandleMappingTuple {
HANDLE handle;
void* mapping;
};
DECLARE_VECTOR(HandleMappingList, struct HandleMappingTuple);
DEFINE_VECTOR(HandleMappingList, struct HandleMappingTuple);
#endif
struct VFileFD {
struct VFile d;
int fd;
#ifdef _WIN32
HANDLE hMap;
struct HandleMappingList handles;
#endif
};
@ -74,12 +86,23 @@ struct VFile* VFileFromFD(int fd) {
vfd->d.truncate = _vfdTruncate;
vfd->d.size = _vfdSize;
vfd->d.sync = _vfdSync;
#ifdef _WIN32
HandleMappingListInit(&vfd->handles, 4);
#endif
return &vfd->d;
}
bool _vfdClose(struct VFile* vf) {
struct VFileFD* vfd = (struct VFileFD*) vf;
#ifdef _WIN32
size_t i;
for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) {
UnmapViewOfFile(HandleMappingListGetPointer(&vfd->handles, i)->mapping);
CloseHandle(HandleMappingListGetPointer(&vfd->handles, i)->handle);
}
HandleMappingListDeinit(&vfd->handles);
#endif
if (close(vfd->fd) < 0) {
return false;
}
@ -134,16 +157,25 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
if (size > fileSize) {
size = fileSize;
}
vfd->hMap = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0);
return MapViewOfFile(vfd->hMap, mapFiles, 0, 0, size);
struct HandleMappingTuple tuple = {0};
tuple.handle = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0);
tuple.mapping = MapViewOfFile(tuple.handle, mapFiles, 0, 0, size);
*HandleMappingListAppend(&vfd->handles) = tuple;
return tuple.mapping;
}
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
UNUSED(size);
struct VFileFD* vfd = (struct VFileFD*) vf;
UnmapViewOfFile(memory);
CloseHandle(vfd->hMap);
vfd->hMap = 0;
size_t i;
for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) {
if (HandleMappingListGetPointer(&vfd->handles, i)->mapping == memory) {
UnmapViewOfFile(memory);
CloseHandle(HandleMappingListGetPointer(&vfd->handles, i)->handle);
HandleMappingListShift(&vfd->handles, i, 1);
break;
}
}
}
#endif