| | 1 | /* FCE Ultra - NES/Famicom Emulator |
| | 2 | * |
| | 3 | * Copyright notice for this file: |
| | 4 | * Copyright (C) 1998 BERO |
| | 5 | * Copyright (C) 2002 Ben Parnell |
| | 6 | * |
| | 7 | * This program is free software; you can redistribute it and/or modify |
| | 8 | * it under the terms of the GNU General Public License as published by |
| | 9 | * the Free Software Foundation; either version 2 of the License, or |
| | 10 | * (at your option) any later version. |
| | 11 | * |
| | 12 | * This program is distributed in the hope that it will be useful, |
| | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | 15 | * GNU General Public License for more details. |
| | 16 | * |
| | 17 | * You should have received a copy of the GNU General Public License |
| | 18 | * along with this program; if not, write to the Free Software |
| | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | 20 | */ |
| | 21 | #include <string.h> |
| | 22 | #include "types.h" |
| | 23 | #include "x6502.h" |
| | 24 | |
| | 25 | #include "fce.h" |
| | 26 | #include "sound.h" |
| | 27 | #include "netplay.h" |
| | 28 | #include "svga.h" |
| | 29 | |
| | 30 | #include "input.h" |
| | 31 | #include "state.h" |
| | 32 | #include "movie.h" |
| | 33 | |
| | 34 | #include "fds.h" |
| | 35 | #include "vsuni.h" |
| | 36 | #include "dprintf.h" |
| | 37 | |
| | 38 | extern INPUTC *FCEU_InitZapper(int w); |
| | 39 | extern INPUTC *FCEU_InitPowerpadA(int w); |
| | 40 | extern INPUTC *FCEU_InitArkanoid(int w); |
| | 41 | |
| | 42 | extern INPUTCFC *FCEU_InitArkanoidFC(void); |
| | 43 | extern INPUTCFC *FCEU_InitSpaceShadow(void); |
| | 44 | extern INPUTCFC *FCEU_InitFKB(void); |
| | 45 | static uint8 joy_readbit[2]; |
| | 46 | static uint8 joy[4]={0,0,0,0}; |
| | 47 | static uint8 LastStrobe; |
| | 48 | |
| | 49 | static int FSDisable=0; /* Set to 1 if NES-style four-player adapter is disabled. */ |
| | 50 | static int JPAttrib[2]={0,0}; |
| | 51 | static int JPType[2]={0,0}; |
| | 52 | static void *InputDataPtr[2]; |
| | 53 | |
| | 54 | static int JPAttribFC=0; |
| | 55 | static int JPTypeFC=0; |
| | 56 | static void *InputDataPtrFC; |
| | 57 | |
| | 58 | void (*InputScanlineHook)(uint8 *bg, uint8 *spr, uint32 linets, int final); |
| | 59 | |
| | 60 | static INPUTC DummyJPort={0,0,0,0,0}; |
| | 61 | static INPUTC *JPorts[2]={&DummyJPort,&DummyJPort}; |
| | 62 | static INPUTCFC *FCExp=0; |
| | 63 | |
| | 64 | /* This function is a quick hack to get the NSF player to use emulated gamepad |
| | 65 | input. |
| | 66 | */ |
| | 67 | uint8 FCEU_GetJoyJoy(void) |
| | 68 | { |
| | 69 | return(joy[0]|joy[1]|joy[2]|joy[3]); |
| | 70 | } |
| | 71 | |
| | 72 | static uint8 FP_FASTAPASS(1) ReadGPVS(int w) |
| | 73 | { |
| | 74 | uint8 ret=0; |
| | 75 | |
| | 76 | if(joy_readbit[w]>=8) |
| | 77 | ret=1; |
| | 78 | else |
| | 79 | { |
| | 80 | ret = ((joy[w]>>(joy_readbit[w]))&1); |
| | 81 | joy_readbit[w]++; |
| | 82 | } |
| | 83 | return ret; |
| | 84 | } |
| | 85 | |
| | 86 | static uint8 FP_FASTAPASS(1) ReadGP(int w) |
| | 87 | { |
| | 88 | uint8 ret; |
| | 89 | //if(JoyMulti) |
| | 90 | //{ |
| | 91 | //ret = ((joy[w]>>(joy_readbit[w]))&1)| |
| | 92 | //(((joy[w]>>(joy_readbit[w]+8))&1)<<1); |
| | 93 | //if(joy_readbit[w]>8) ret=0; |
| | 94 | //} |
| | 95 | ret = ((joy[w]>>(joy_readbit[w]))&1); |
| | 96 | if(FSDisable) |
| | 97 | { |
| | 98 | if(joy_readbit[w]>=8) ret|=1; |
| | 99 | } |
| | 100 | else |
| | 101 | { |
| | 102 | if(joy_readbit[w]==19-w) ret|=1; |
| | 103 | } |
| | 104 | joy_readbit[w]++; |
| | 105 | return ret; |
| | 106 | } |
| | 107 | |
| | 108 | static DECLFR(JPRead) |
| | 109 | { |
| | 110 | uint8 ret=0; |
| | 111 | |
| | 112 | if(JPorts[A&1]->Read) |
| | 113 | ret|=JPorts[A&1]->Read(A&1); |
| | 114 | |
| | 115 | if(FCExp) |
| | 116 | if(FCExp->Read) |
| | 117 | ret=FCExp->Read(A&1,ret); |
| | 118 | |
| | 119 | ret|=X.DB&0xC0; |
| | 120 | dprintf("JPRead %i %02x", A&1, ret); |
| | 121 | return(ret); |
| | 122 | } |
| | 123 | |
| | 124 | static DECLFW(B4016) |
| | 125 | { |
| | 126 | if(FCExp) |
| | 127 | if(FCExp->Write) |
| | 128 | FCExp->Write(V&7); |
| | 129 | |
| | 130 | if(JPorts[0]->Write) |
| | 131 | JPorts[0]->Write(V&1); |
| | 132 | if(JPorts[1]->Write) |
| | 133 | JPorts[1]->Write(V&1); |
| | 134 | |
| | 135 | if((LastStrobe&1) && (!(V&1))) |
| | 136 | { |
| | 137 | /* This strobe code is just for convenience. If it were |
| | 138 | with the code in input / *.c, it would more accurately represent |
| | 139 | what's really going on. But who wants accuracy? ;) |
| | 140 | Seriously, though, this shouldn't be a problem. |
| | 141 | */ |
| | 142 | if(JPorts[0]->Strobe) |
| | 143 | JPorts[0]->Strobe(0); |
| | 144 | if(JPorts[1]->Strobe) |
| | 145 | JPorts[1]->Strobe(1); |
| | 146 | if(FCExp) |
| | 147 | if(FCExp->Strobe) |
| | 148 | FCExp->Strobe(); |
| | 149 | } |
| | 150 | LastStrobe=V&1; |
| | 151 | } |
| | 152 | |
| | 153 | static void FP_FASTAPASS(1) StrobeGP(int w) |
| | 154 | { |
| | 155 | joy_readbit[w]=0; |
| | 156 | } |
| | 157 | |
| | 158 | static INPUTC GPC={ReadGP,0,StrobeGP,0,0,0}; |
| | 159 | static INPUTC GPCVS={ReadGPVS,0,StrobeGP,0,0,0}; |
| | 160 | |
| | 161 | void DrawInput(uint8 *buf) |
| | 162 | { |
| | 163 | int x; |
| | 164 | |
| | 165 | for(x=0;x<2;x++) |
| | 166 | if(JPorts[x]->Draw) |
| | 167 | JPorts[x]->Draw(x,buf,JPAttrib[x]); |
| | 168 | if(FCExp) |
| | 169 | if(FCExp->Draw) |
| | 170 | FCExp->Draw(buf,JPAttribFC); |
| | 171 | } |
| | 172 | |
| | 173 | void UpdateInput(void) |
| | 174 | { |
| | 175 | int x; |
| | 176 | |
| | 177 | for(x=0;x<2;x++) |
| | 178 | { |
| | 179 | switch(JPType[x]) |
| | 180 | { |
| | 181 | case SI_GAMEPAD: |
| | 182 | if(!x) joy[0]=*(uint16 *)InputDataPtr[0]; |
| | 183 | else joy[1]=*(uint16 *)InputDataPtr[1]; |
| | 184 | break; |
| | 185 | default: |
| | 186 | if(JPorts[x]->Update) |
| | 187 | JPorts[x]->Update(x,InputDataPtr[x],JPAttrib[x]); |
| | 188 | break; |
| | 189 | } |
| | 190 | } |
| | 191 | if(FCExp) |
| | 192 | if(FCExp->Update) |
| | 193 | FCExp->Update(InputDataPtrFC,JPAttribFC); |
| | 194 | |
| | 195 | if(FCEUGameInfo.type==GIT_VSUNI) |
| | 196 | { |
| | 197 | uint16 t=joy[0]; |
| | 198 | joy[0]=(joy[0]&0xC)|(joy[1]&0xF3); |
| | 199 | joy[1]=(joy[1]&0xC)|(t&0xF3); |
| | 200 | if(coinon) coinon--; |
| | 201 | } |
| | 202 | #ifdef NETWORK |
| | 203 | if(netplay) NetplayUpdate(&joy[0],&joy[1]); |
| | 204 | #endif |
| | 205 | if (current < 0) FCEUMOV_AddJoy(joy); |
| | 206 | else framecount++; // for debug |
| | 207 | //FlushCommandQueue(); |
| | 208 | } |
| | 209 | |
| | 210 | static DECLFR(VSUNIRead0) |
| | 211 | { |
| | 212 | uint8 ret=0; |
| | 213 | |
| | 214 | if(JPorts[0]->Read) |
| | 215 | ret|=(JPorts[0]->Read(0))&1; |
| | 216 | |
| | 217 | ret|=(vsdip&3)<<3; |
| | 218 | if(coinon) |
| | 219 | ret|=0x4; |
| | 220 | return ret; |
| | 221 | } |
| | 222 | |
| | 223 | static DECLFR(VSUNIRead1) |
| | 224 | { |
| | 225 | uint8 ret=0; |
| | 226 | |
| | 227 | if(JPorts[1]->Read) |
| | 228 | ret|=(JPorts[1]->Read(1))&1; |
| | 229 | ret|=vsdip&0xFC; |
| | 230 | return ret; |
| | 231 | } |
| | 232 | |
| | 233 | static void SLHLHook(uint8 *bg, uint8 *spr, uint32 linets, int final) |
| | 234 | { |
| | 235 | int x; |
| | 236 | |
| | 237 | for(x=0;x<2;x++) |
| | 238 | if(JPorts[x]->SLHook) |
| | 239 | JPorts[x]->SLHook(x,bg,spr,linets,final); |
| | 240 | if(FCExp) |
| | 241 | if(FCExp->SLHook) |
| | 242 | FCExp->SLHook(bg,spr,linets,final); |
| | 243 | } |
| | 244 | |
| | 245 | static void CheckSLHook(void) |
| | 246 | { |
| | 247 | InputScanlineHook=0; |
| | 248 | if(JPorts[0]->SLHook || JPorts[1]->SLHook) |
| | 249 | InputScanlineHook=SLHLHook; |
| | 250 | if(FCExp) |
| | 251 | if(FCExp->SLHook) |
| | 252 | InputScanlineHook=SLHLHook; |
| | 253 | } |
| | 254 | |
| | 255 | static void FASTAPASS(1) SetInputStuff(int x) |
| | 256 | { |
| | 257 | switch(JPType[x]) |
| | 258 | { |
| | 259 | case SI_GAMEPAD: |
| | 260 | if(FCEUGameInfo.type==GIT_VSUNI) |
| | 261 | JPorts[x]=&GPCVS; |
| | 262 | else |
| | 263 | JPorts[x]=&GPC; |
| | 264 | break; |
| | 265 | case SI_ARKANOID:JPorts[x]=FCEU_InitArkanoid(x);break; |
| | 266 | case SI_ZAPPER:JPorts[x]=FCEU_InitZapper(x);break; |
| | 267 | case SI_POWERPADA:JPorts[x]=FCEU_InitPowerpadA(x);break; |
| | 268 | case SI_NONE:JPorts[x]=&DummyJPort;break; |
| | 269 | } |
| | 270 | |
| | 271 | CheckSLHook(); |
| | 272 | } |
| | 273 | |
| | 274 | static uint8 F4ReadBit[2]; |
| | 275 | static void StrobeFami4(void) |
| | 276 | { |
| | 277 | F4ReadBit[0]=F4ReadBit[1]=0; |
| | 278 | } |
| | 279 | |
| | 280 | static uint8 FP_FASTAPASS(2) ReadFami4(int w, uint8 ret) |
| | 281 | { |
| | 282 | ret&=1; |
| | 283 | |
| | 284 | ret |= ((joy[w]>>(F4ReadBit[w]+8))&1)<<1; |
| | 285 | if(F4ReadBit[w]>=8) ret|=2; |
| | 286 | else F4ReadBit[w]++; |
| | 287 | |
| | 288 | return(ret); |
| | 289 | } |
| | 290 | |
| | 291 | static INPUTCFC FAMI4C={ReadFami4,0,StrobeFami4,0,0,0}; |
| | 292 | static void SetInputStuffFC(void) |
| | 293 | { |
| | 294 | switch(JPTypeFC) |
| | 295 | { |
| | 296 | case SIFC_NONE:FCExp=0;break; |
| | 297 | case SIFC_ARKANOID:FCExp=FCEU_InitArkanoidFC();break; |
| | 298 | case SIFC_SHADOW:FCExp=FCEU_InitSpaceShadow();break; |
| | 299 | case SIFC_4PLAYER:FCExp=&FAMI4C;memset(&F4ReadBit,0,sizeof(F4ReadBit));break; |
| | 300 | case SIFC_FKB:FCExp=FCEU_InitFKB();break; |
| | 301 | } |
| | 302 | CheckSLHook(); |
| | 303 | } |
| | 304 | |
| | 305 | // VS Unisystem code called after SetInputMap() hooks B4016. Need to |
| | 306 | // rewrite code to make this more sane? |
| | 307 | |
| | 308 | void InitializeInput(void) |
| | 309 | { |
| | 310 | memset(joy_readbit,0,sizeof(joy_readbit)); |
| | 311 | memset(joy,0,sizeof(joy)); |
| | 312 | LastStrobe=0; |
| | 313 | |
| | 314 | if(FCEUGameInfo.type==GIT_VSUNI) |
| | 315 | { |
| | 316 | SetReadHandler(0x4016,0x4016,VSUNIRead0); |
| | 317 | SetReadHandler(0x4017,0x4017,VSUNIRead1); |
| | 318 | } |
| | 319 | else |
| | 320 | SetReadHandler(0x4016,0x4017,JPRead); |
| | 321 | SetWriteHandler(0x4016,0x4016,B4016); |
| | 322 | |
| | 323 | SetInputStuff(0); |
| | 324 | SetInputStuff(1); |
| | 325 | SetInputStuffFC(); |
| | 326 | } |
| | 327 | |
| | 328 | void FCEUI_SetInput(int port, int type, void *ptr, int attrib) |
| | 329 | { |
| | 330 | JPAttrib[port]=attrib; |
| | 331 | JPType[port]=type; |
| | 332 | InputDataPtr[port]=ptr; |
| | 333 | SetInputStuff(port); |
| | 334 | } |
| | 335 | |
| | 336 | void FCEUI_DisableFourScore(int s) |
| | 337 | { |
| | 338 | FSDisable=s; |
| | 339 | } |
| | 340 | |
| | 341 | void FCEUI_SetInputFC(int type, void *ptr, int attrib) |
| | 342 | { |
| | 343 | JPAttribFC=attrib; |
| | 344 | JPTypeFC=type; |
| | 345 | InputDataPtrFC=ptr; |
| | 346 | SetInputStuffFC(); |
| | 347 | } |
| | 348 | |
| | 349 | void FCEU_DoSimpleCommand(int cmd) |
| | 350 | { |
| | 351 | printf("FCEU_DoSimpleCommand: %i\n", cmd); |
| | 352 | switch(cmd) |
| | 353 | { |
| | 354 | case FCEUNPCMD_FDSINSERT: FCEU_FDSInsert();break; |
| | 355 | case FCEUNPCMD_FDSSELECT: FCEU_FDSSelect();break; |
| | 356 | // case FCEUNPCMD_FDSEJECT: FCEU_FDSEject();break; |
| | 357 | case FCEUNPCMD_VSUNICOIN: FCEU_VSUniCoin(); break; |
| | 358 | case FCEUNPCMD_VSUNIDIP0 ... (FCEUNPCMD_VSUNIDIP0 + 7): FCEU_VSUniToggleDIP(cmd - FCEUNPCMD_VSUNIDIP0);break; |
| | 359 | case FCEUNPCMD_POWER: PowerNES();break; |
| | 360 | case FCEUNPCMD_RESET: ResetNES();break; |
| | 361 | default: printf("FCEU_DoSimpleCommand: can't handle cmd %i\n", cmd); break; |
| | 362 | } |
| | 363 | } |
| | 364 | |
| | 365 | void FCEU_QSimpleCommand(int cmd) |
| | 366 | { |
| | 367 | // if(FCEUnetplay) |
| | 368 | // FCEUNET_SendCommand(cmd, 0); |
| | 369 | // else |
| | 370 | { |
| | 371 | // totally wrong: |
| | 372 | // if(!FCEUMOV_IsPlaying()) |
| | 373 | // FCEU_DoSimpleCommand(cmd); |
| | 374 | // else |
| | 375 | // FCEUMOV_AddCommand(cmd); |
| | 376 | |
| | 377 | FCEU_DoSimpleCommand(cmd); |
| | 378 | // if(FCEUMOV_IsRecording()) |
| | 379 | // FCEUMOV_AddCommand(cmd); |
| | 380 | } |
| | 381 | } |
| | 382 | |
| | 383 | void FCEUI_FDSSelect(void) |
| | 384 | { |
| | 385 | FCEU_QSimpleCommand(FCEUNPCMD_FDSSELECT); |
| | 386 | } |
| | 387 | |
| | 388 | int FCEUI_FDSInsert(void) |
| | 389 | { |
| | 390 | FCEU_QSimpleCommand(FCEUNPCMD_FDSINSERT); |
| | 391 | return(1); |
| | 392 | } |
| | 393 | |
| | 394 | /* |
| | 395 | int FCEUI_FDSEject(void) |
| | 396 | { |
| | 397 | FCEU_QSimpleCommand(FCEUNPCMD_FDSEJECT); |
| | 398 | return(1); |
| | 399 | } |
| | 400 | */ |
| | 401 | void FCEUI_VSUniToggleDIP(int w) |
| | 402 | { |
| | 403 | FCEU_QSimpleCommand(FCEUNPCMD_VSUNIDIP0 + w); |
| | 404 | } |
| | 405 | |
| | 406 | void FCEUI_VSUniCoin(void) |
| | 407 | { |
| | 408 | FCEU_QSimpleCommand(FCEUNPCMD_VSUNICOIN); |
| | 409 | } |
| | 410 | |
| | 411 | void FCEUI_ResetNES(void) |
| | 412 | { |
| | 413 | FCEU_QSimpleCommand(FCEUNPCMD_RESET); |
| | 414 | } |
| | 415 | |
| | 416 | void FCEUI_PowerNES(void) |
| | 417 | { |
| | 418 | FCEU_QSimpleCommand(FCEUNPCMD_POWER); |
| | 419 | } |
| | 420 | |
| | 421 | |
| | 422 | |
| | 423 | SFORMAT FCEUCTRL_STATEINFO[]={ |
| | 424 | { joy_readbit, 2, "JYRB"}, |
| | 425 | { joy, 4, "JOYS"}, |
| | 426 | { &LastStrobe, 1, "LSTS"}, |
| | 427 | { 0 } |
| | 428 | }; |
| | 429 | |
| | 430 | |