snes, add justifier support for lethal enforcers

This commit is contained in:
dinkc64 2024-12-15 02:00:02 -05:00
parent b3291e88fa
commit f78d51fb61
6 changed files with 138 additions and 49 deletions

View File

@ -773,6 +773,7 @@ int BurnComputeSHA1(const UINT8 *buffer, int buffer_size, char *hash_str);
#define HARDWARE_FDS (HARDWARE_PREFIX_FDS)
#define HARDWARE_SNES (HARDWARE_PREFIX_SNES)
#define HARDWARE_SNES_ZAPPER (HARDWARE_PREFIX_SNES | 0x0000001)
#define HARDWARE_SNES_JUSTIFIER (HARDWARE_PREFIX_SNES | 0x0000002)
#define HARDWARE_CHANNELF (HARDWARE_PREFIX_CHANNELF)

View File

@ -16,7 +16,7 @@ static UINT8 DrvRecalc = 1;
static UINT8 LastControllerDip = 0;
static UINT8 LastControllerTimer = 0;
static INT32 has_gun = 0;
static INT32 has_gun = 0; // 1 Zapper (SuperScope), 2 Justifier
static ButtonToggle scope_turbo;
static ButtonToggle scope_pause;
@ -139,6 +139,38 @@ static struct BurnInputInfo SNESZapperInputList[] = {
STDINPUTINFO(SNESZapper)
static struct BurnInputInfo SNESJustifierInputList[] = {
{"P1 Up", BIT_DIGITAL, snesInputPort0 + 4, "p1 up" },
{"P1 Down", BIT_DIGITAL, snesInputPort0 + 5, "p1 down" },
{"P1 Left", BIT_DIGITAL, snesInputPort0 + 6, "p1 left" },
{"P1 Right", BIT_DIGITAL, snesInputPort0 + 7, "p1 right" },
{"P1 Button Y", BIT_DIGITAL, snesInputPort0 + 1, "p1 fire 1" },
{"P1 Button X", BIT_DIGITAL, snesInputPort0 + 9, "p1 fire 2" },
{"P1 Button B", BIT_DIGITAL, snesInputPort0 + 0, "p1 fire 3" },
{"P1 Button A", BIT_DIGITAL, snesInputPort0 + 8, "p1 fire 4" },
{"P1 Button L", BIT_DIGITAL, snesInputPort0 + 10, "p1 fire 5" },
{"P1 Button R", BIT_DIGITAL, snesInputPort0 + 11, "p1 fire 6" },
{"P1 Select", BIT_DIGITAL, snesInputPort0 + 2, "p1 select" },
{"P1 Start", BIT_DIGITAL, snesInputPort0 + 3, "p1 start" },
A("P2 Gun X", BIT_ANALOG_REL, &Analog[0], "p2 x-axis" ),
A("P2 Gun Y", BIT_ANALOG_REL, &Analog[1], "p2 y-axis" ),
{"P2 Fire", BIT_DIGITAL, snesInputPort1 + 7, "p2 fire 1" },
{"P2 Start", BIT_DIGITAL, snesInputPort1 + 5, "p2 fire 2" },
A("P3 Gun X", BIT_ANALOG_REL, &Analog[2], "p3 x-axis" ),
A("P3 Gun Y", BIT_ANALOG_REL, &Analog[3], "p3 y-axis" ),
{"P3 Fire", BIT_DIGITAL, snesInputPort1 + 6, "p3 fire 1" },
{"P3 Start", BIT_DIGITAL, snesInputPort1 + 4, "p3 fire 2" },
{"Reset", BIT_DIGITAL, &DrvReset, "reset" },
{"Dip A", BIT_DIPSWITCH, DrvDips + 0, "dip" },
{"Dip B", BIT_DIPSWITCH, DrvDips + 1, "dip" },
};
STDINPUTINFO(SNESJustifier)
static struct BurnDIPInfo SNESDIPList[] =
{
DIP_OFFSET(0x19)
@ -157,6 +189,15 @@ static struct BurnDIPInfo SNESZapperDIPList[] =
STDDIPINFO(SNESZapper)
static struct BurnDIPInfo SNESJustifierDIPList[] =
{
DIP_OFFSET(0x15)
{0x00, 0xff, 0xff, 0x00, NULL },
{0x01, 0xff, 0xff, 0x00, NULL },
};
STDDIPINFO(SNESJustifier)
static struct BurnDIPInfo SNESMouseBaseDIPList[] =
{
DIP_OFFSET(0x21)
@ -287,6 +328,12 @@ static INT32 DrvInit()
BurnGunInit(1, true);
}
if (BurnDrvGetHardwareCode() == HARDWARE_SNES_JUSTIFIER) {
bprintf(0, _T("*** SNES: With Justifier\n"));
has_gun = 2;
BurnGunInit(2, true);
}
DrvDoReset();
return 0;
@ -358,8 +405,10 @@ static INT32 DrvScan(INT32 nAction, INT32* pnMin)
if (has_gun) {
BurnGunScan();
scope_turbo.Scan();
scope_pause.Scan();
if (has_gun == 1) {
scope_turbo.Scan();
scope_pause.Scan();
}
}
}
@ -386,20 +435,36 @@ static INT32 DrvFrame()
if (has_gun) {
BurnGunMakeInputs(0, Analog[0], Analog[1]);
snesInputPort1[8] = BurnGunReturnX(0);
snesInputPort1[9] = BurnGunReturnY(0);
scope_turbo.Toggle(snesInputPort1[2]);
if (has_gun == 2) { // justifier - 2 guns
BurnGunMakeInputs(1, Analog[2], Analog[3]); // 2nd gun
}
if (has_gun == 1) { // superscope
scope_turbo.Toggle(snesInputPort1[2]);
}
}
if (CheckControllerPlug() == 0) {
INT32 p2_type = 0;
switch (has_gun) {
case 0: p2_type = DEVICE_GAMEPAD; break;
case 1: p2_type = DEVICE_SUPERSCOPE; break;
case 2: p2_type = DEVICE_JUSTIFIER; break;
}
for (INT32 i = 0; i < 12; i++) {
if ((DrvDips[1] & 0x01) == 0) {
snes_setButtonState(snes, 1, i, snesInputPort0[i], DEVICE_GAMEPAD);
}
if ((DrvDips[1] & 0x02) == 0) { // p2 controller or lightgun
snes_setButtonState(snes, 2, i, snesInputPort1[i], has_gun ? DEVICE_SUPERSCOPE : DEVICE_GAMEPAD);
if ((DrvDips[1] & 0x02) == 0) { // p2 controller or lightgun (SuperScope, Justifier)
snes_setButtonState(snes, 2, i, snesInputPort1[i], p2_type);
}
}
if (has_gun) {
snes_setGunState(snes, BurnGunReturnX(0), BurnGunReturnY(0), (p2_type == DEVICE_JUSTIFIER) ? BurnGunReturnX(1) : 0, (p2_type == DEVICE_JUSTIFIER) ? BurnGunReturnY(1) : 0);
}
if (DrvDips[1] & 0x01) {
snes_setMouseState(snes, 1, Analog[0], Analog[1], snesMouseButtons[0], snesMouseButtons[1]);
}
@ -14393,8 +14458,8 @@ struct BurnDriver BurnDrvsnes_Lethalenf = {
"snes_lethalenf", NULL, NULL, NULL, "1993",
"Lethal Enforcers (USA)\0", NULL, "Konami", "Nintendo",
NULL, NULL, NULL, NULL,
BDF_GAME_WORKING, 2, HARDWARE_SNES, GBF_SHOOT, 0,
SNESGetZipName, snes_LethalenfRomInfo, snes_LethalenfRomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
BDF_GAME_WORKING, 2, HARDWARE_SNES_JUSTIFIER, GBF_SHOOT, 0,
SNESGetZipName, snes_LethalenfRomInfo, snes_LethalenfRomName, NULL, NULL, NULL, NULL, SNESJustifierInputInfo, SNESJustifierDIPInfo,
DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x8000,
512, 448, 4, 3
};

View File

@ -16,7 +16,6 @@ Input* input_init(Snes* snes, int8_t tag) {
input->portTag = tag; // 1, 2, ...
input->type = DEVICE_GAMEPAD;
input->currentState = 0;
// TODO: handle I/O line (and latching of PPU)
return input;
}
@ -30,11 +29,11 @@ void input_reset(Input* input) {
input->currentState = 0;
input->lastX = 0;
input->lastY = 0;
input->mouseSens = 0;
input->devParam = 0;
}
void input_handleState(Input* input, StateHandler* sh) {
sh_handleBytes(sh, &input->type, &input->lastX, &input->lastY, &input->mouseSens, NULL);
sh_handleBytes(sh, &input->type, &input->lastX, &input->lastY, &input->devParam, NULL);
sh_handleBools(sh, &input->latchLine, NULL);
sh_handleInts(sh, &input->currentState, &input->latchedState, NULL);
}
@ -54,22 +53,27 @@ void input_setMouse(Input* input, int16_t x, int16_t y, uint8_t buttonA, uint8_t
x = d_min(d_abs(x), 0x7f);
y = d_min(d_abs(y), 0x7f);
input->currentState = 0;
input->currentState |= (0x01 | (input->mouseSens % 3) << 4 | (buttonA) << 6 | (buttonB) << 7) << 16;
input->currentState |= (0x01 | (input->devParam % 3) << 4 | (buttonA) << 6 | (buttonB) << 7) << 16;
input->currentState |= (y | input->lastY) << 8;
input->currentState |= (x | input->lastX) << 0;
}
static void update_mouse_sensitivity(Input* input) {
input->currentState = (input->currentState & ~0x300000) | ((input->mouseSens % 3) << (4 + 16));
input->currentState = (input->currentState & ~0x300000) | ((input->devParam % 3) << (4 + 16));
}
void input_latch(Input* input, bool value) {
// if(input->latchLine && !value) {
if(value) {
input->latchedState = input->currentState;
if (input->type == DEVICE_MOUSE) {
if (input->type == DEVICE_MOUSE) {
update_mouse_sensitivity(input);
}
} else
if (input->type == DEVICE_JUSTIFIER) {
// every other read selects between p1/p2's gun. they're daisy-chained,
// and plugged into port 2 on the snes/sfc
input->devParam ^= 1;
input->latchedState |= (input->devParam & 1) << 3;
}
}
input->latchLine = value;
}
@ -88,9 +92,10 @@ uint8_t input_read(Input* input) {
if (input->latchLine) {
// fun feature: reading the mouse while latched changes the sensitivty
// setting
update_mouse_sensitivity(input);
input->mouseSens++;
}
update_mouse_sensitivity(input);
input->devParam++;
} // fallthrough!
case DEVICE_JUSTIFIER: // below is shared w/DEVICE_MOUSE
ret = (input->latchedState >> 31) & 1;
input->latchedState <<= 1;
input->latchedState |= 1;

View File

@ -10,8 +10,9 @@ typedef struct Input Input;
#include "snes.h"
#include "statehandler.h"
enum { DEVICE_NONE = 0, DEVICE_GAMEPAD = 1, DEVICE_SUPERSCOPE = 2, DEVICE_MOUSE = 3 };
enum { DEVICE_NONE = 0, DEVICE_GAMEPAD = 1, DEVICE_SUPERSCOPE = 2, DEVICE_MOUSE = 3, DEVICE_JUSTIFIER = 4 };
enum { SCOPE_FIRE = 1 << 0, SCOPE_CURSOR = 1 << 1, SCOPE_TURBO = 1 << 2, SCOPE_PAUSE = 1 << 3, SCOPE_RELOAD = 1 << 6 };
enum { JUSTI_FIRE1 = 1 << 7, JUSTI_FIRE2 = 1 << 6 };
struct Input {
Snes* snes;
@ -24,7 +25,7 @@ struct Input {
uint32_t latchedState;
// for mouse
uint8_t lastX, lastY;
uint8_t mouseSens;
uint8_t devParam; // mouse: sensitivity setting, gun: Justifier ID
};
Input* input_init(Snes* snes, int8_t tag);

View File

@ -91,6 +91,7 @@ void snes_runSpcCycle(Snes* snes);
bool snes_loadRom(Snes* snes, const uint8_t* data, int length, uint8_t* biosdata, int bioslength);
void snes_setButtonState(Snes* snes, int player, int button, int pressed, int device);
void snes_setMouseState(Snes* snes, int player, int16_t x, int16_t y, uint8_t buttonA, uint8_t buttonB);
void snes_setGunState(Snes* snes, int x1, int y1, int x2, int y2);
void snes_setPixels(Snes* snes, uint8_t* pixelData, int height);
void snes_setSamples(Snes* snes, int16_t* sampleData, int samplesPerFrame);
int snes_saveBattery(Snes* snes, uint8_t* data);

View File

@ -73,7 +73,7 @@ bool snes_loadRom(Snes* snes, const uint8_t* data, int length, uint8_t* biosdata
max = headers[i].score;
used = i;
}
}
}
bprintf(0, _T("header used %d\n"), used);
if(used & 1) {
// odd-numbered ones are for headered roms
@ -197,34 +197,50 @@ bool snes_isPal(Snes* snes) {
}
void snes_setButtonState(Snes* snes, int player, int button, int pressed, int device) {
// set key in controller
Input* input = (player == 1) ? snes->input1 : snes->input2;
uint32_t *c_state = &input->currentState;
// set key in controller
Input* input = (player == 1) ? snes->input1 : snes->input2;
uint32_t *c_state = &input->currentState;
input_setType(input, device);
input_setType(input, device);
if(pressed) {
*c_state |= 1 << button;
} else {
*c_state &= ~(1 << button);
if(pressed) {
*c_state |= 1 << button;
} else {
*c_state &= ~(1 << button);
}
}
#define is_gun_offscreen(x,y) (!(x == 0 || x == 255 || y == 0 || y == 255) )
void snes_setGunState(Snes* snes, int x1, int y1, int x2, int y2) {
// set gun coords
Input* input = snes->input2; // gun always in input 2
uint32_t *c_state = &input->currentState;
switch (input->type) {
case DEVICE_SUPERSCOPE:
*c_state |= 0xff00;
if (*c_state & SCOPE_FIRE || *c_state & SCOPE_CURSOR) {
ppu_latchScope(x1, y1);
}
break;
case DEVICE_JUSTIFIER:
*c_state |= 0xe5500; // Justifier serial bit stream "header"
switch (input->devParam & 1) {
case 0:
if (is_gun_offscreen(x2, y2)) {
ppu_latchScope(x2, y2);
}
break;
case 1:
if (is_gun_offscreen(x1, y1)) {
ppu_latchScope(x1, y1);
}
break;
}
if (device == DEVICE_SUPERSCOPE) {
static uint8_t button8_9[2] = { 0, 0 };
switch (button) {
case 8:
case 9:
button8_9[button & 1] = pressed;
break;
case 11: // last button
*c_state |= 0xff00;
if (*c_state & SCOPE_FIRE || *c_state & SCOPE_CURSOR) {
ppu_latchScope(button8_9[0], button8_9[1]);
}
break;
}
}
break;
}
}
void snes_setMouseState(Snes* snes, int player, int16_t x, int16_t y, uint8_t buttonA, uint8_t buttonB) {