how much it's capable of. [Shamus]
- Fix coming out of fullscreen on Win32. As of now, it doesn't restore the
window position correctly [CJ]
+- Fix the Tripper Getem ROM so that it works. It has some tight coupling
+ between the M68K and the DSP that causes the DSP to starve itself; fixing
+ this will probably fix a bunch of other timing related issues as well.
+ [Shamus]
Stuff that was added/fixed
Virtual Jaguar v2.1.1 GCC/Qt
----------------------------
+* Added controller profiles. What this means is that if you left your gamepad
+ behind and/or have a different one, VJ will let you create a controller
+ config for the new controller without destroying the old one. This also means
+ that if you have *no* controllers, you can still play VJ with the keyboard.
+ [Shamus]
+* Fixed a few problems with the DSP a timing; as a result, the sound in Rayman
+ and the FACTS demo is correct now. [Shamus]
+* Removed toolbar from full screen mode. [Shamus]
+* Added analog inputs to gamepad support. [Shamus]
* Fixed 6MB cartridge space access limitation. 6MB carts should work now.
[Shamus]
* Fixed problem with JERRY interrupts corrupting the M68K core. [Shamus]
static void dsp_opcode_div(void)
{
+#if 0
uint32_t _Rm=RM;
uint32_t _Rn=RN;
}
else
RN=0xffffffff;
+#else
+ if (RM)
+ {
+ if (dsp_div_control & 0x01) // 16.16 division
+ {
+ dsp_remain = ((uint64_t)RN << 16) % RM;
+ RN = ((uint64_t)RN << 16) / RM;
+ }
+ else
+ {
+ // We calculate the remainder first because we destroy RN after
+ // this by assigning it to itself.
+ dsp_remain = RN % RM;
+ RN = RN / RM;
+ }
+
+// What we really should do here is figure out why this condition
+// happens in the real divide unit and emulate *that* behavior.
+#if 0
+ if ((gpu_remain - RM) & 0x80000000) // If the result would have been negative...
+ gpu_remain -= RM; // Then make it negative!
+#endif
+ }
+ else
+ RN = 0xFFFFFFFF;
+#endif
}
static void dsp_opcode_imultn(void)
#define GPU_DIS_SUBQT
#define GPU_DIS_XOR
-//bool doGPUDis = false;
-bool doGPUDis = true;
+bool doGPUDis = false;
+//bool doGPUDis = true;
#endif
/*
/* gpu_flag_c = (gpu_flag_c ? 1 : 0);
gpu_flag_z = (gpu_flag_z ? 1 : 0);
gpu_flag_n = (gpu_flag_n ? 1 : 0);*/
+#if 0
+if (gpu_pc == 0xF03200)
+ doGPUDis = true;
+#endif
uint16_t opcode = GPUReadWord(gpu_pc, GPU);
uint32_t index = opcode >> 10;
WriteLog("%06X: MULT R%02u, R%02u [NCZ:%u%u%u, R%02u=%08X, R%02u=%08X] -> ", gpu_pc-2, IMM_1, IMM_2, gpu_flag_n, gpu_flag_c, gpu_flag_z, IMM_1, RM, IMM_2, RN);
#endif
RN = (uint16_t)RM * (uint16_t)RN;
+// RN = (RM & 0xFFFF) * (RN & 0xFFFF);
SET_ZN(RN);
#ifdef GPU_DIS_MULT
if (doGPUDis)
// The original tried to get it right by checking to see if the
// remainder was negative, but that's too late...
// The code there should do it now, but I'm not 100% sure...
+// [Now it should be correct, but not displaying correct behavior of the actual
+// hardware. A step in the right direction.]
if (RM)
{
if (gpu_div_control & 0x01) // 16.16 division
{
- RN = ((uint64_t)RN << 16) / RM;
gpu_remain = ((uint64_t)RN << 16) % RM;
+ RN = ((uint64_t)RN << 16) / RM;
}
else
{
- RN = RN / RM;
+ // We calculate the remainder first because we destroy RN after
+ // this by assigning it to itself.
gpu_remain = RN % RM;
+ RN = RN / RM;
}
+// What we really should do here is figure out why this condition
+// happens in the real divide unit and emulate *that* behavior.
+#if 0
if ((gpu_remain - RM) & 0x80000000) // If the result would have been negative...
gpu_remain -= RM; // Then make it negative!
+#endif
}
else
RN = 0xFFFFFFFF;
-/* uint32_t _RM=RM;
- uint32_t _RN=RN;
-
- if (_RM)
- {
- if (gpu_div_control & 1)
- {
- gpu_remain = (((uint64_t)_RN) << 16) % _RM;
- if (gpu_remain&0x80000000)
- gpu_remain-=_RM;
- RN = (((uint64_t)_RN) << 16) / _RM;
- }
- else
- {
- gpu_remain = _RN % _RM;
- if (gpu_remain&0x80000000)
- gpu_remain-=_RM;
- RN/=_RM;
- }
- }
- else
- RN=0xffffffff;*/
#ifdef GPU_DIS_DIV
if (doGPUDis)
WriteLog("[NCZ:%u%u%u, R%02u=%08X, R%02u=%08X] Remainder: %08X\n", gpu_flag_n, gpu_flag_c, gpu_flag_z, IMM_1, RM, IMM_2, RN, gpu_remain);
// Here's the main application loop--short and simple...
int main(int argc, char * argv[])
{
- // Win32 console redirection, because MS and their band of super geniuses decided
- // that nobody would ever launch an app from the command line. :-P
+ // Win32 console redirection, because MS and their band of super geniuses
+ // decided that nobody would ever launch an app from the command line. :-P
+ // [Unfortunately, this doesn't seem to work on Vista/7. :-(]
#ifdef __GCCWIN32__
BOOL (WINAPI * AttachConsole)(DWORD dwProcessId);
ControllerTab::ControllerTab(QWidget * parent/*= 0*/): QWidget(parent),
label(new QLabel(tr("Controller:"))),
- profile(new QComboBox(this)),
+ profileList(new QComboBox(this)),
redefineAll(new QPushButton(tr("Define All Inputs"))),
controllerWidget(new ControllerWidget(this))
{
QHBoxLayout * top = new QHBoxLayout;
layout->addLayout(top);
top->addWidget(label);
- top->addWidget(profile, 0, Qt::AlignLeft);
+ top->addWidget(profileList, 0, Qt::AlignLeft);
layout->addWidget(controllerWidget);
layout->addWidget(redefineAll, 0, Qt::AlignHCenter);
-// layout->setFixedWidth(label->width());
-// layout->setSizeConstraint(QLayout::SetFixedSize);
-// top->setSizeConstraint(QLayout::SetFixedSize);
-//printf("cw width = %i, label width = %i (min=%i, sizehint=%i)\n", controllerWidget->width(), label->width(), label->minimumWidth(), label->sizeHint().width());
- // This is ugly, ugly, ugly. But it works. :-P It's a shame that Qt's
- // layout system doesn't seem to allow for a nicer way to handle this.
-// profile->setFixedWidth(controllerWidget->sizeHint().width() - label->sizeHint().width());
setLayout(layout);
+ // At least by doing this, it keeps the QComboBox from resizing itself too
+ // large and breaking the layout. :-P
setFixedWidth(sizeHint().width());
connect(redefineAll, SIGNAL(clicked()), this, SLOT(DefineAllKeys()));
+ connect(profileList, SIGNAL(currentIndexChanged(int)), this, SLOT(ChangeProfile(int)));
-//this is the default. :-/ need to set it somewhere else i guess...
-// profile->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
- profile->addItem(tr("Keyboard"));
+ // Set up the profile combobox (Keyboard is the default, and always
+ // present)
+ profileList->addItem(tr("Keyboard"));
for(int i=0; i<Gamepad::numJoysticks; i++)
- profile->addItem(Gamepad::GetJoystickName(i));
+ profileList->addItem(Gamepad::GetJoystickName(i));
}
}
-//QSize ControllerTab::sizeHint(void) const
-//{
-// return
-//}
-
-
void ControllerTab::DefineAllKeys(void)
{
// char jagButtonName[21][10] = { "Up", "Down", "Left", "Right",
}
}
+
+void ControllerTab::ChangeProfile(int profile)
+{
+printf("You selected profile: %s\n", (profile == 0 ? "Keyboard" : Gamepad::GetJoystickName(profile - 1)));
+}
+
+#if 0
+The profiles need the following:
+
+ - The name of the controller
+ - A unique human readable ID
+ - The key definitions for that controller (keyboard keys can be mixed in)
+
+So there can be more than one profile for each unique controller; the
+relationship is many-to-one. So basically, how it works it like this: SDL
+reports all connected controllers. If there are none connected, the default
+controller is the keyboard (which can have multiple profiles). The UI only
+presents those profiles which are usuable with the controllers that are plugged
+in, all else is ignored. The user can pick the profile for the controller and
+configure the keys for it; the UI automagically saves everything.
+
+How to handle the case of identical controllers being plugged in? How does the
+UI know which is which? Each controller will have a mapping to a default
+Jaguar controller (#1 or #2). Still doesn't prevent confusion though. Actually,
+it can: The profile can have a field that maps it to a preferred Jaguar
+controller, which can also be both (#1 AND #2--in this case we can set it to
+zero which means no preference). If the UI detects two of the same controller
+and each can be mapped to the same profile, it assigns them in order since it
+doesn't matter, the profiles are identical.
+
+The default profile is always available and is the keyboard (hey, we're PC
+centric here). The default profile is usually #0.
+
+Can there be more than one keyboard profile? Why not? You will need separate
+ones for controller #1 and controller #2.
+
+A profile might look like this:
+
+Field 1: Nostomo N45 Analog
+Field 2: Dad's #1
+Field 3: Jaguar controller #1
+Field 4: The button/stick mapping
+
+Profile # would be implicit in the order that they are stored in the internal
+data structure.
+
+When a new controller is plugged in with no profiles attached, it defaults to
+a set keyboard layout which the user can change. So every new controller will
+always have at least one profile.
+#endif
+
public:
ControllerTab(QWidget * parent = 0);
~ControllerTab();
-// QSize sizeHint(void) const;
protected slots:
void DefineAllKeys(void);
+ void ChangeProfile(int);
private:
QLabel * label;
- QComboBox * profile;
+ QComboBox * controllerList;
+ QComboBox * profileList;
QPushButton * redefineAll;
public:
ControllerWidget * controllerWidget;
+ int profile;
};
#endif // __CONTROLLERTAB_H__
{
if (e->key() == (int)vjs.p1KeyBindings[i])
// joypad_0_buttons[i] = (uint8)state;
- joypad_0_buttons[i] = (state ? 0x01 : 0x00);
+ joypad0Buttons[i] = (state ? 0x01 : 0x00);
// Pad #2 is screwing up pad #1. Prolly a problem in joystick.cpp...
// So let's try to fix it there. :-P [DONE]
if (e->key() == (int)vjs.p2KeyBindings[i])
// joypad_1_buttons[i] = (uint8)state;
- joypad_1_buttons[i] = (state ? 0x01 : 0x00);
+ joypad1Buttons[i] = (state ? 0x01 : 0x00);
}
}
for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
{
if (vjs.p1KeyBindings[i] & (JOY_BUTTON | JOY_HAT | JOY_AXIS))
- joypad_0_buttons[i] = (Gamepad::GetState(0, vjs.p1KeyBindings[i]) ? 0x01 : 0x00);
+ joypad0Buttons[i] = (Gamepad::GetState(0, vjs.p1KeyBindings[i]) ? 0x01 : 0x00);
if (vjs.p2KeyBindings[i] & (JOY_BUTTON | JOY_HAT | JOY_AXIS))
- joypad_1_buttons[i] = (Gamepad::GetState(1, vjs.p2KeyBindings[i]) ? 0x01 : 0x00);
+ joypad1Buttons[i] = (Gamepad::GetState(1, vjs.p2KeyBindings[i]) ? 0x01 : 0x00);
}
}
// else if (offset >= 0xF17C00 && offset <= 0xF17C01)
// return anajoy_byte_read(offset);
else if (offset >= 0xF14000 && offset <= 0xF14003)
- return JoystickReadByte(offset) | EepromReadByte(offset);
+// return JoystickReadByte(offset) | EepromReadByte(offset);
+ return JoystickReadWord(offset & 0xFE) | EepromReadByte(offset);
else if (offset >= 0xF14000 && offset <= 0xF1A0FF)
return EepromReadByte(offset);
}*/
else if ((offset >= 0xF14000) && (offset <= 0xF14003))
{
- JoystickWriteByte(offset, data);
+// JoystickWriteByte(offset, data);
+ JoystickWriteWord(offset & 0xFE, (uint16_t)data);
EepromWriteByte(offset, data);
return;
}
//
// by cal2
// GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Caz (BeOS)
-// Cleanups/fixes by James Hammons
-// (C) 2010 Underground Software
+// Extensive rewrite by James Hammons
+// (C) 2013 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
//
#include "joystick.h"
-
-//#include <SDL.h>
-//#include <time.h>
#include <string.h> // For memset()
#include "gpu.h"
#include "jaguar.h"
// Global vars
static uint8_t joystick_ram[4];
-uint8_t joypad_0_buttons[21];
-uint8_t joypad_1_buttons[21];
-
-bool keyBuffer[21];
+uint8_t joypad0Buttons[21];
+uint8_t joypad1Buttons[21];
+bool audioEnabled = false;
+bool joysticksEnabled = false;
-//SDL_Joystick * joystick1;
bool GUIKeyHeld = false;
extern int start_logging;
void JoystickReset(void)
{
memset(joystick_ram, 0x00, 4);
- memset(joypad_0_buttons, 0, 21);
- memset(joypad_1_buttons, 0, 21);
+ memset(joypad0Buttons, 0, 21);
+ memset(joypad1Buttons, 0, 21);
}
}
-uint8_t JoystickReadByte(uint32_t offset)
+//uint8_t JoystickReadByte(uint32_t offset)
+uint16_t JoystickReadWord(uint32_t offset)
{
-// For now, until we can fix the 2nd controller... :-P
-//memset(joypad_1_buttons, 0, 21);
+ // E, D, B, 7
+ uint8_t joypad0Offset[16] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0xFF, 0xFF, 0xFF, 0x08, 0xFF, 0x04, 0x00, 0xFF
+ };
+ uint8_t joypad1Offset[16] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x04, 0xFF, 0x08, 0x0C, 0xFF
+ };
#warning "No bounds checking done in JoystickReadByte!"
-// extern bool hardwareTypeNTSC;
offset &= 0x03;
if (offset == 0)
{
+#if 0
uint8_t data = 0x00;
int pad0Index = joystick_ram[1] & 0x0F;
int pad1Index = (joystick_ram[1] >> 4) & 0x0F;
else if (!(pad1Index & 0x08))
pad1Index = 0;
- if (joypad_0_buttons[(pad0Index << 2) + 0]) data |= 0x01;
- if (joypad_0_buttons[(pad0Index << 2) + 1]) data |= 0x02;
- if (joypad_0_buttons[(pad0Index << 2) + 2]) data |= 0x04;
- if (joypad_0_buttons[(pad0Index << 2) + 3]) data |= 0x08;
- if (joypad_1_buttons[(pad1Index << 2) + 0]) data |= 0x10;
- if (joypad_1_buttons[(pad1Index << 2) + 1]) data |= 0x20;
- if (joypad_1_buttons[(pad1Index << 2) + 2]) data |= 0x40;
- if (joypad_1_buttons[(pad1Index << 2) + 3]) data |= 0x80;
+ if (joypad0Buttons[(pad0Index << 2) + 0]) data |= 0x01;
+ if (joypad0Buttons[(pad0Index << 2) + 1]) data |= 0x02;
+ if (joypad0Buttons[(pad0Index << 2) + 2]) data |= 0x04;
+ if (joypad0Buttons[(pad0Index << 2) + 3]) data |= 0x08;
+ if (joypad1Buttons[(pad1Index << 2) + 0]) data |= 0x10;
+ if (joypad1Buttons[(pad1Index << 2) + 1]) data |= 0x20;
+ if (joypad1Buttons[(pad1Index << 2) + 2]) data |= 0x40;
+ if (joypad1Buttons[(pad1Index << 2) + 3]) data |= 0x80;
return ~data;
+#else
+ if (!joysticksEnabled)
+ return 0xFF;
+
+ // Joystick data returns active low for buttons pressed, high for non-
+ // pressed.
+ uint16_t data = 0xFFFF;
+ uint8_t offset0 = joypad0Offset[joystick_ram[1] & 0x0F];
+ uint8_t offset1 = joypad1Offset[(joystick_ram[1] >> 4) & 0x0F];
+
+ if (offset0 != 0xFF)
+ {
+ uint16_t mask[4] = { 0xFEFF, 0xFDFF, 0xFBFF, 0xF7FF };
+
+ for(uint8_t i=0; i<4; i++)
+ data &= (joypad0Buttons[offset0 + i] ? mask[i] : 0xFFFF);
+ }
+
+ if (offset1 != 0xFF)
+ {
+ uint16_t mask[4] = { 0xEFFF, 0xDFFF, 0xBFFF, 0x7FFF };
+
+ for(uint8_t i=0; i<4; i++)
+ data &= (joypad1Buttons[offset1 + i] ? mask[i] : 0xFFFF);
+ }
+
+ return data;
+#endif
}
- else if (offset == 3)
+// else if (offset == 3)
+ else if (offset == 2)
{
// Hardware ID returns NTSC/PAL identification bit here
- uint8_t data = 0x2F | (vjs.hardwareTypeNTSC ? 0x10 : 0x00);
+ uint16_t data = 0xFFEF | (vjs.hardwareTypeNTSC ? 0x10 : 0x00);
+
+ if (!joysticksEnabled)
+ return data;
+#if 0
int pad0Index = joystick_ram[1] & 0x0F;
int pad1Index = (joystick_ram[1] >> 4) & 0x0F;
#warning "!!! This reports TeamTap incorrectly when PAUSE pressed on controller #1 or #2 !!!"
if (!(pad0Index & 0x01))
{
- if (joypad_0_buttons[BUTTON_PAUSE])
+ if (joypad0Buttons[BUTTON_PAUSE])
data ^= 0x01;
- if (joypad_0_buttons[BUTTON_A])
+ if (joypad0Buttons[BUTTON_A])
data ^= 0x02;
}
else if (!(pad0Index & 0x02))
{
- if (joypad_0_buttons[BUTTON_B])
+ if (joypad0Buttons[BUTTON_B])
data ^= 0x02;
}
else if (!(pad0Index & 0x04))
{
- if (joypad_0_buttons[BUTTON_C])
+ if (joypad0Buttons[BUTTON_C])
data ^= 0x02;
}
else if (!(pad0Index & 0x08))
{
- if (joypad_0_buttons[BUTTON_OPTION])
+ if (joypad0Buttons[BUTTON_OPTION])
data ^= 0x02;
}
if (!(pad1Index & 0x08))
{
- if (joypad_1_buttons[BUTTON_PAUSE])
+ if (joypad1Buttons[BUTTON_PAUSE])
data ^= 0x04;
- if (joypad_1_buttons[BUTTON_A])
+ if (joypad1Buttons[BUTTON_A])
data ^= 0x08;
}
else if (!(pad1Index & 0x04))
{
- if (joypad_1_buttons[BUTTON_B])
+ if (joypad1Buttons[BUTTON_B])
data ^= 0x08;
}
else if (!(pad1Index & 0x02))
{
- if (joypad_1_buttons[BUTTON_C])
+ if (joypad1Buttons[BUTTON_C])
data ^= 0x08;
}
else if (!(pad1Index & 0x01))
{
- if (joypad_1_buttons[BUTTON_OPTION])
+ if (joypad1Buttons[BUTTON_OPTION])
data ^= 0x08;
}
+#else
+ // Joystick data returns active low for buttons pressed, high for non-
+ // pressed.
+ uint8_t offset0 = joypad0Offset[joystick_ram[1] & 0x0F] / 4;
+ uint8_t offset1 = joypad1Offset[(joystick_ram[1] >> 4) & 0x0F] / 4;
+
+ if (offset0 != 0xFF)
+ {
+ uint8_t mask[4][2] = { { BUTTON_A, BUTTON_PAUSE }, { BUTTON_B, -1 }, { BUTTON_C, -1 }, { BUTTON_OPTION, -1 } };
+ data &= (joypad0Buttons[mask[offset0][0]] ? 0xFFFD : 0xFFFF);
+
+ if (mask[offset0][1] != -1)
+ data &= (joypad0Buttons[mask[offset0][1]] ? 0xFFFE : 0xFFFF);
+ }
+
+ if (offset1 != 0xFF)
+ {
+ uint8_t mask[4][2] = { { BUTTON_A, BUTTON_PAUSE }, { BUTTON_B, -1 }, { BUTTON_C, -1 }, { BUTTON_OPTION, -1 } };
+ data &= (joypad1Buttons[mask[offset1][0]] ? 0xFFF7 : 0xFFFF);
+
+ if (mask[offset1][1] != -1)
+ data &= (joypad1Buttons[mask[offset1][1]] ? 0xFFFB : 0xFFFF);
+ }
+#endif
return data;
}
- return joystick_ram[offset];
+// return joystick_ram[offset];
+ return 0xFF;
}
+#if 0
uint16_t JoystickReadWord(uint32_t offset)
{
return ((uint16_t)JoystickReadByte((offset + 0) & 0x03) << 8) | JoystickReadByte((offset + 1) & 0x03);
}
+#endif
+#if 0
void JoystickWriteByte(uint32_t offset, uint8_t data)
{
joystick_ram[offset & 0x03] = data;
}
+#endif
void JoystickWriteWord(uint32_t offset, uint16_t data)
offset &= 0x03;
joystick_ram[offset + 0] = (data >> 8) & 0xFF;
joystick_ram[offset + 1] = data & 0xFF;
+
+ if (offset == 0)
+ {
+ audioEnabled = (data & 0x0100 ? true : false);
+ joysticksEnabled = (data & 0x8000 ? true : false);
+ }
}
void JoystickInit(void);
void JoystickReset(void);
void JoystickDone(void);
-void JoystickWriteByte(uint32_t, uint8_t);
+//void JoystickWriteByte(uint32_t, uint8_t);
void JoystickWriteWord(uint32_t, uint16_t);
-uint8_t JoystickReadByte(uint32_t);
+//uint8_t JoystickReadByte(uint32_t);
uint16_t JoystickReadWord(uint32_t);
void JoystickExec(void);
-extern bool keyBuffer[];
-extern uint8_t joypad_0_buttons[];
-extern uint8_t joypad_1_buttons[];
-#endif
+extern uint8_t joypad0Buttons[];
+extern uint8_t joypad1Buttons[];
+extern bool audioEnabled;
+extern bool joysticksEnabled;
+
+#endif // __JOYSTICK_H__
+