a3a99790eb3261990d20b27c7d674cec58cf1ce3
[fceu.git] / input.c
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 "movie.h"
32
33 #include "dprintf.h"
34
35 extern INPUTC *FCEU_InitZapper(int w);
36 extern INPUTC *FCEU_InitPowerpad(int w);
37 extern INPUTC *FCEU_InitArkanoid(int w);
38
39 extern INPUTCFC *FCEU_InitArkanoidFC(void);
40 extern INPUTCFC *FCEU_InitSpaceShadow(void);
41 extern INPUTCFC *FCEU_InitFKB(void);
42 static uint8 joy_readbit[2];
43 static uint16 joy[4]={0,0,0,0};
44
45 extern int coinon;
46
47 static int FSDisable=0; /* Set to 1 if NES-style four-player adapter is disabled. */
48 static int JPAttrib[2]={0,0};
49 static int JPType[2]={0,0};
50 static void *InputDataPtr[2];
51
52 static int JPAttribFC=0;
53 static int JPTypeFC=0;
54 static void *InputDataPtrFC;
55
56 void (*InputScanlineHook)(uint8 *buf, int line);
57
58 static INPUTC DummyJPort={0,0,0,0,0};
59 static INPUTC *JPorts[2]={&DummyJPort,&DummyJPort};
60 static INPUTCFC *FCExp=0;
61
62 static uint8 FP_FASTAPASS(1) ReadGPVS(int w)
63 {
64                 uint8 ret=0;
65
66                 if(joy_readbit[w]>=8)
67                  ret=1;
68                 else
69                 {
70                  ret = ((joy[w]>>(joy_readbit[w]))&1);
71                  joy_readbit[w]++;
72                 }
73                 return ret;
74 }
75
76 static uint8 FP_FASTAPASS(1) ReadGP(int w)
77 {
78                 uint8 ret;
79                 //if(JoyMulti)
80                 //{
81                  //ret = ((joy[w]>>(joy_readbit[w]))&1)|
82                  //(((joy[w]>>(joy_readbit[w]+8))&1)<<1);
83                  //if(joy_readbit[w]>8) ret=0;
84                 //}
85                 ret = ((joy[w]>>(joy_readbit[w]))&1);
86                 if(FSDisable)
87                 {
88                  if(joy_readbit[w]>=8) ret|=1;
89                 }
90                 else
91                 {
92                  if(joy_readbit[w]==19-w) ret|=1;
93                 }
94                 joy_readbit[w]++;
95                 return ret;
96 }
97
98 static DECLFR(JPRead)
99 {
100         uint8 ret=0;
101
102         if(JPorts[A&1]->Read)
103          ret|=JPorts[A&1]->Read(A&1);
104
105         if(FCExp)
106          if(FCExp->Read)
107           ret=FCExp->Read(A&1,ret);
108
109         ret|=X.DB&0xC0;
110         dprintf("JPRead %02x", ret);
111         return(ret);
112 }
113
114 static DECLFW(B4016)
115 {
116         if(FCExp)
117          if(FCExp->Write)
118           FCExp->Write(V&7);
119
120         if(JPorts[0]->Write)
121          JPorts[0]->Write(V&1);
122         if(JPorts[1]->Write)
123          JPorts[1]->Write(V&1);
124
125         if((PSG[0x16]&1) && (!(V&1)))
126         {
127          /* This strobe code is just for convenience.  If it were
128             with the code in input / *.c, it would more accurately represent
129             what's really going on.  But who wants accuracy? ;)
130             Seriously, though, this shouldn't be a problem.
131          */
132          if(JPorts[0]->Strobe)
133           JPorts[0]->Strobe(0);
134          if(JPorts[1]->Strobe)
135           JPorts[1]->Strobe(1);
136          if(FCExp)
137           if(FCExp->Strobe)
138            FCExp->Strobe();
139          }
140          PSG[0x16]=V;
141 }
142
143 static void FP_FASTAPASS(1) StrobeGP(int w)
144 {
145         joy_readbit[w]=0;
146 }
147
148 static INPUTC GPC={ReadGP,0,StrobeGP,0,0,0};
149 static INPUTC GPCVS={ReadGPVS,0,StrobeGP,0,0,0};
150
151 void DrawInput(uint8 *buf)
152 {
153  int x;
154
155  for(x=0;x<2;x++)
156   if(JPorts[x]->Draw)
157    JPorts[x]->Draw(x,buf,JPAttrib[x]);
158  if(FCExp)
159   if(FCExp->Draw)
160    FCExp->Draw(buf,JPAttribFC);
161 }
162
163 void UpdateInput(void)
164 {
165         int x;
166
167         for(x=0;x<2;x++)
168         {
169          switch(JPType[x])
170          {
171           case SI_GAMEPAD:
172                 if(!x) joy[0]=*(uint16 *)InputDataPtr[0];
173                 else joy[1]=*(uint16 *)InputDataPtr[1];
174                 break;
175           default:
176                 if(JPorts[x]->Update)
177                  JPorts[x]->Update(x,InputDataPtr[x],JPAttrib[x]);
178                 break;
179          }
180         }
181         if(FCExp)
182          if(FCExp->Update)
183           FCExp->Update(InputDataPtrFC,JPAttribFC);
184
185         if(FCEUGameInfo.type==GIT_VSUNI)
186         {
187          uint16 t=joy[0];
188          joy[0]=(joy[0]&0xC)|(joy[1]&0xF3);
189          joy[1]=(joy[1]&0xC)|(t&0xF3);
190          if(coinon) coinon--;
191         }
192         #ifdef NETWORK
193         if(netplay) NetplayUpdate(&joy[0],&joy[1]);
194         #endif
195         if (current < 0) FCEUMOV_AddJoy(joy);
196         FlushCommandQueue();
197 }
198
199 static DECLFR(VSUNIRead0)
200 {
201         uint8 ret=0;
202
203         if(JPorts[0]->Read)
204          ret|=(JPorts[0]->Read(0))&1;
205
206         ret|=(vsdip&3)<<3;
207         if(coinon)
208          ret|=0x4;
209         return ret;
210 }
211
212 static DECLFR(VSUNIRead1)
213 {
214         uint8 ret=0;
215
216         if(JPorts[1]->Read)
217          ret|=(JPorts[1]->Read(1))&1;
218         ret|=vsdip&0xFC;
219         return ret;
220 }
221
222 static void SLHLHook(uint8 *buf, int line)
223 {
224  int x;
225
226  for(x=0;x<2;x++)
227   if(JPorts[x]->SLHook)
228    JPorts[x]->SLHook(x,buf,line);
229  if(FCExp)
230   if(FCExp->SLHook)
231    FCExp->SLHook(buf,line);
232 }
233
234 static void CheckSLHook(void)
235 {
236         InputScanlineHook=0;
237         if(JPorts[0]->SLHook || JPorts[1]->SLHook)
238          InputScanlineHook=SLHLHook;
239         if(FCExp)
240          if(FCExp->SLHook)
241           InputScanlineHook=SLHLHook;
242 }
243
244 static void FASTAPASS(1) SetInputStuff(int x)
245 {
246          switch(JPType[x])
247          {
248           case SI_GAMEPAD:
249            if(FCEUGameInfo.type==GIT_VSUNI)
250             JPorts[x]=&GPCVS;
251            else
252             JPorts[x]=&GPC;
253           break;
254           case SI_ARKANOID:JPorts[x]=FCEU_InitArkanoid(x);break;
255           case SI_ZAPPER:JPorts[x]=FCEU_InitZapper(x);break;
256           case SI_POWERPAD:JPorts[x]=FCEU_InitPowerpad(x);break;
257           case SI_NONE:JPorts[x]=&DummyJPort;break;
258          }
259
260         CheckSLHook();
261 }
262
263 static uint8 F4ReadBit[2];
264 static void StrobeFami4(void)
265 {
266  F4ReadBit[0]=F4ReadBit[1]=0;
267 }
268
269 static uint8 FP_FASTAPASS(2) ReadFami4(int w, uint8 ret)
270 {
271  ret&=1;
272
273  ret |= ((joy[w]>>(F4ReadBit[w]+8))&1)<<1;
274  if(F4ReadBit[w]>=8) ret|=2;
275  else F4ReadBit[w]++;
276
277  return(ret);
278 }
279
280 static INPUTCFC FAMI4C={ReadFami4,0,StrobeFami4,0,0,0};
281 static void SetInputStuffFC(void)
282 {
283         switch(JPTypeFC)
284         {
285          case SIFC_NONE:FCExp=0;break;
286          case SIFC_ARKANOID:FCExp=FCEU_InitArkanoidFC();break;
287          case SIFC_SHADOW:FCExp=FCEU_InitSpaceShadow();break;
288          case SIFC_4PLAYER:FCExp=&FAMI4C;memset(&F4ReadBit,0,sizeof(F4ReadBit));break;
289          case SIFC_FKB:FCExp=FCEU_InitFKB();break;
290         }
291         CheckSLHook();
292 }
293
294 // VS Unisystem code called after SetInputMap() hooks B4016.  Need to
295 // rewrite code to make this more sane?
296
297 void InitializeInput(void)
298 {
299         memset(joy_readbit,0,sizeof(joy_readbit));
300         memset(joy,0,sizeof(joy));
301
302         if(FCEUGameInfo.type==GIT_VSUNI)
303         {
304          SetReadHandler(0x4016,0x4016,VSUNIRead0);
305          SetReadHandler(0x4017,0x4017,VSUNIRead1);
306         }
307         else
308          SetReadHandler(0x4016,0x4017,JPRead);
309         SetWriteHandler(0x4016,0x4016,B4016);
310
311         SetInputStuff(0);
312         SetInputStuff(1);
313         SetInputStuffFC();
314 }
315
316 void FCEUI_SetInput(int port, int type, void *ptr, int attrib)
317 {
318  JPAttrib[port]=attrib;
319  JPType[port]=type;
320  InputDataPtr[port]=ptr;
321  SetInputStuff(port);
322 }
323
324 void FCEUI_DisableFourScore(int s)
325 {
326  FSDisable=s;
327 }
328
329 void FCEUI_SetInputFC(int type, void *ptr, int attrib)
330 {
331  JPAttribFC=attrib;
332  JPTypeFC=type;
333  InputDataPtrFC=ptr;
334  SetInputStuffFC();
335 }