/* Mednafen - Multi-system Emulator * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../psx.h" #include "../frontio.h" #include "justifier.h" #include "../../mednafen-endian.h" class InputDevice_Justifier : public InputDevice { public: InputDevice_Justifier(void); virtual ~InputDevice_Justifier(); virtual void Power(void); virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name); virtual void UpdateInput(const void *data); virtual bool RequireNoFrameskip(void); // GPULineHook modified to take upscale_factor for color detection (surf_pitchinpix unused) virtual int32_t GPULineHook(const int32_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider, const unsigned surf_pitchinpix, const unsigned upscale_factor); // // // virtual void SetDTR(bool new_dtr); virtual bool GetDSR(void); virtual bool Clock(bool TxD, int32 &dsr_pulse_delay); private: bool dtr; uint8 buttons; bool trigger_eff; bool trigger_noclear; bool need_hit_detect; int16 nom_x, nom_y; int32 os_shot_counter; bool prev_oss; int32 command_phase; uint32 bitpos; uint8 receive_buffer; uint8 command; uint8 transmit_buffer[16]; uint32 transmit_pos; uint32 transmit_count; // // Video timing stuff bool prev_vsync; int line_counter; }; InputDevice_Justifier::InputDevice_Justifier(void) { Power(); } InputDevice_Justifier::~InputDevice_Justifier() { } void InputDevice_Justifier::Power(void) { dtr = 0; buttons = 0; trigger_eff = 0; trigger_noclear = 0; need_hit_detect = false; nom_x = 0; nom_y = 0; os_shot_counter = 0; prev_oss = 0; command_phase = 0; bitpos = 0; receive_buffer = 0; command = 0; memset(transmit_buffer, 0, sizeof(transmit_buffer)); transmit_pos = 0; transmit_count = 0; prev_vsync = 0; line_counter = 0; } void InputDevice_Justifier::UpdateInput(const void *data) { uint8 *d8 = (uint8 *)data; nom_x = (int16)MDFN_de16lsb(&d8[0]); nom_y = (int16)MDFN_de16lsb(&d8[2]); trigger_noclear = (bool)(d8[4] & 0x1); trigger_eff |= trigger_noclear; buttons = (d8[4] >> 1) & 0x3; if(os_shot_counter > 0) // FIXME if UpdateInput() is ever called more than once per video frame(at ~50 or ~60Hz). os_shot_counter--; if((d8[4] & 0x8) && !prev_oss && os_shot_counter == 0) os_shot_counter = 10; prev_oss = d8[4] & 0x8; } int InputDevice_Justifier::StateAction(StateMem* sm, int load, int data_only, const char* section_name) { SFORMAT StateRegs[] = { SFVAR(dtr), SFVAR(buttons), SFVAR(trigger_eff), SFVAR(trigger_noclear), SFVAR(need_hit_detect), SFVAR(nom_x), SFVAR(nom_y), SFVAR(os_shot_counter), SFVAR(prev_oss), SFVAR(command_phase), SFVAR(bitpos), SFVAR(receive_buffer), SFVAR(command), SFARRAY(transmit_buffer, sizeof(transmit_buffer)), SFVAR(transmit_pos), SFVAR(transmit_count), SFVAR(prev_vsync), SFVAR(line_counter), SFEND }; int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name); if(load) { if(((uint64_t)transmit_pos + transmit_count) > sizeof(transmit_buffer)) { transmit_pos = 0; transmit_count = 0; } } return(ret); } bool InputDevice_Justifier::RequireNoFrameskip(void) { return(true); } int32_t InputDevice_Justifier::GPULineHook(const int32_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider, const unsigned surf_pitchinpix, const unsigned upscale_factor) { int32_t ret = PSX_EVENT_MAXTS; if(vsync && !prev_vsync) line_counter = 0; if(pixels && pix_clock) { const int avs = 16; // Not 16 for PAL, fixme. int32 gx; int32 gy; int32 gxa; gx = (nom_x * 2 + pix_clock_divider) / (pix_clock_divider * 2); gy = nom_y; gxa = gx; // - (pix_clock / 400000); //if(gxa < 0 && gx >= 0) // gxa = 0; if(!os_shot_counter && need_hit_detect && gxa >= 0 && gxa < (int)width && line_counter >= (avs + gy - 1) && line_counter <= (avs + gy + 1)) { int r, g, b, a; format->DecodeColor(pixels[gxa * upscale_factor], r, g, b, a); if((r + g + b) >= 0x40) // Wrong, but not COMPLETELY ABSOLUTELY wrong, at least. ;) { ret = timestamp + (int64)(gxa + pix_clock_offset) * (44100 * 768) / pix_clock - 177; } } chair_x = gx; chair_y = (avs + gy) - line_counter; } line_counter++; return(ret); } void InputDevice_Justifier::SetDTR(bool new_dtr) { if(!dtr && new_dtr) { command_phase = 0; bitpos = 0; transmit_pos = 0; transmit_count = 0; } else if(dtr && !new_dtr) { //if(bitpos || transmit_count) // printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); } dtr = new_dtr; } bool InputDevice_Justifier::GetDSR(void) { if(!dtr) return(0); if(!bitpos && transmit_count) return(1); return(0); } bool InputDevice_Justifier::Clock(bool TxD, int32 &dsr_pulse_delay) { bool ret = 1; dsr_pulse_delay = 0; if(!dtr) return(1); if(transmit_count) ret = (transmit_buffer[transmit_pos] >> bitpos) & 1; receive_buffer &= ~(1 << bitpos); receive_buffer |= TxD << bitpos; bitpos = (bitpos + 1) & 0x7; if(!bitpos) { //printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase); if(transmit_count) { transmit_pos++; transmit_count--; } switch(command_phase) { case 0: if(receive_buffer != 0x01) command_phase = -1; else { transmit_buffer[0] = 0x31; transmit_pos = 0; transmit_count = 1; command_phase++; } break; case 2: //if(receive_buffer) // printf("%02x\n", receive_buffer); command_phase++; break; case 3: need_hit_detect = receive_buffer & 0x10; // TODO, see if it's (val&0x10) == 0x10, or some other mask value. command_phase++; break; case 1: command = receive_buffer; command_phase++; transmit_buffer[0] = 0x5A; //if(command != 0x42) // fprintf(stderr, "Justifier unhandled command: 0x%02x\n", command); //assert(command == 0x42); if(command == 0x42) { transmit_buffer[1] = 0xFF ^ ((buttons & 2) << 2); transmit_buffer[2] = 0xFF ^ (trigger_eff << 7) ^ ((buttons & 1) << 6); if(os_shot_counter > 0) { transmit_buffer[2] |= (1 << 7); if(os_shot_counter == 6 || os_shot_counter == 5) { transmit_buffer[2] &= ~(1 << 7); } } transmit_pos = 0; transmit_count = 3; trigger_eff = trigger_noclear; } else { command_phase = -1; transmit_buffer[1] = 0; transmit_buffer[2] = 0; transmit_pos = 0; transmit_count = 0; } break; } } if(!bitpos && transmit_count) dsr_pulse_delay = 200; return(ret); } InputDevice *Device_Justifier_Create(void) { return new InputDevice_Justifier(); } InputDeviceInputInfoStruct Device_Justifier_IDII[6] = { { "x_axis", "X Axis", -1, IDIT_X_AXIS }, { "y_axis", "Y Axis", -1, IDIT_Y_AXIS }, { "trigger", "Trigger", 0, IDIT_BUTTON, NULL }, { "o", "O", 1, IDIT_BUTTON, NULL }, { "start", "Start", 2, IDIT_BUTTON, NULL }, { "offscreen_shot", "Offscreen Shot(Simulated)", 3, IDIT_BUTTON, NULL }, };