a09b4880e5144658d126bdc14d3be70e20a9bc59
[fceu.git] / boards / onebus.c
1 /* FCE Ultra - NES/Famicom Emulator\r
2  *\r
3  * Copyright notice for this file:\r
4  *  Copyright (C) 2007-2010 CaH4e3\r
5  *\r
6  * This program is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
19  *\r
20  * OneBus system\r
21  * Street Dance (Dance pad) (Unl)\r
22  * 101-in-1 Arcade Action II\r
23  * DreamGEAR 75-in-1\r
24  */\r
25 \r
26 #include "mapinc.h"\r
27 \r
28 static uint8 isDance;\r
29 static uint8 regs[16],regc[6];\r
30 static uint8 IRQCount,IRQLatch,IRQa, IRQReload, pcm_enable = 0, pcm_irq = 0;\r
31 static int16 pcm_addr, pcm_size, pcm_latch, pcm_clock = 0xF6;\r
32 static writefunc old4011write, old4012write, old4013write, old4015write;\r
33 static readfunc old4015read;\r
34 \r
35 static SFORMAT StateRegs[]=\r
36 {\r
37   {regc, 6, "REGC"},\r
38   {regs, 16, "REGS"},\r
39   {&IRQReload, 1, "IRQR"},\r
40   {&IRQCount, 1, "IRQC"},\r
41   {&IRQLatch, 1, "IRQL"},\r
42   {&IRQa, 1, "IRQA"},\r
43   {&pcm_enable, 1, "PCME"},\r
44   {&pcm_irq, 1, "PCMIRQ"},\r
45   {&pcm_addr, 2, "PCMADDR"},\r
46   {&pcm_size, 2, "PCMSIZE"},\r
47   {&pcm_latch, 2, "PCMLATCH"},\r
48   {&pcm_clock, 2, "PCMCLOCK"},\r
49   {0}\r
50 };\r
51 \r
52 static void Sync(void)\r
53 {\r
54   uint16 cswap = (regs[0xf] & 0x80) << 5;\r
55   uint16 pswap = (regs[0xd]&1)?((regs[0xf] & 0x40) << 8):0;\r
56   uint16 pbase = (regs[0]&0xf0)<<4;\r
57   uint16 cbase = (((regs[0]&0x0f)<<8)|(regs[0xc]<<1)|((regs[0xd]&0xf8)>>3))<<3;\r
58   uint16 pmask = 0x3f>>(regs[0xb]&0xf);\r
59 \r
60   setchr1(cswap^0x0000,cbase|(regc[0]&(~1)));\r
61   setchr1(cswap^0x0400,cbase|(regc[0]|1));\r
62   setchr1(cswap^0x0800,cbase|(regc[1]&(-1)));\r
63   setchr1(cswap^0x0c00,cbase|(regc[1]|1));\r
64   setchr1(cswap^0x1000,cbase|(regc[2]));\r
65   setchr1(cswap^0x1400,cbase|(regc[3]));\r
66   setchr1(cswap^0x1800,cbase|(regc[4]));\r
67   setchr1(cswap^0x1c00,cbase|(regc[5]));\r
68 \r
69   if(regs[0xd]&2)\r
70   {\r
71     setprg8(pswap^0x8000, pbase|(regs[0x7]&pmask)|(regs[0xa]&(~pmask)));\r
72     setprg8(      0xA000, pbase|(regs[0x8]&pmask)|(regs[0xa]&(~pmask)));\r
73     setprg8(pswap^0xC000, pbase|(regs[0x9]&pmask)|(regs[0xa]&(~pmask)));\r
74     setprg8(      0xE000, pbase|regs[0xa]);\r
75   }\r
76   else\r
77   {\r
78     setprg8(pswap^0x8000, pbase|(regs[0x7]&pmask)|(regs[0xa]&(~pmask)));\r
79     setprg8(      0xA000, pbase|(regs[0x8]&pmask)|(regs[0xa]&(~pmask)));\r
80     setprg8(pswap^0xC000, pbase|((~1)&pmask)|(regs[0xa]&(~pmask)));\r
81     setprg8(      0xE000, pbase|((~0)&pmask)|(regs[0xa]&(~pmask)));\r
82   }\r
83 \r
84   setmirror(regs[0xe]);\r
85 }\r
86 \r
87 static DECLFW(UNLOneBusWrite20XX)\r
88 {\r
89 //  FCEU_printf("PPU %04x:%04x\n",A,V);\r
90   if(A == 0x201A)\r
91     regs[0xd] = V;\r
92   else if(A == 0x2018)\r
93     regs[0xc] = V;\r
94   Sync();\r
95 }\r
96 \r
97 static DECLFW(UNLOneBusWriteExp)\r
98 {\r
99 //  FCEU_printf("EXP %04x:%04x\n",A,V);\r
100 //  switch(A & 0x0F)\r
101 //  {\r
102 //  case 2: pcm_latch = pcm_clock; FCEU_printf("write %04x:%04x\n",A,V); break;\r
103 //  case 3: pcm_irqa = 0; X6502_IRQEnd(FCEU_IQEXT); pcm_irq = 0; FCEU_printf("write %04x:%04x\n",A,V); break;\r
104 //  case 4: pcm_irqa = 1; FCEU_printf("write %04x:%04x\n",A,V); break;\r
105 //  default:\r
106    regs[A & 0x0F] = V;\r
107    Sync();\r
108 //  }\r
109 }\r
110 \r
111 static DECLFW(UNLOneBusWriteDebug)\r
112 {\r
113 //  FCEU_printf("write %04x:%04x\n",A,V);\r
114 }\r
115 \r
116 static DECLFW(UNLOneBusWriteMMC)\r
117 {\r
118 //  FCEU_printf("MMC %04x:%04x\n",A,V);\r
119   switch(A&0xE001)\r
120   {\r
121   case 0x8000: regs[0xf] = V; Sync(); break;\r
122   case 0x8001:\r
123   {\r
124     uint8 mask = 0xff, mmc3cmd = regs[0xf]&7;\r
125     switch(mmc3cmd)\r
126     {\r
127     case 0:\r
128     case 1:\r
129     case 2:\r
130     case 3:\r
131     case 4:\r
132     case 5:\r
133       if(regs[0xd]&4)\r
134         mask = 0x0f;\r
135       else\r
136         mask >>= ((regs[0xb]&0xf0)>>4);\r
137       regc[mmc3cmd] = V&mask;\r
138       break;\r
139     case 6:\r
140     case 7:\r
141       mask = (mask&0x3f)>>(regs[0xb]&0xf);\r
142       regs[mmc3cmd+1] = (regs[mmc3cmd+1]&(~mask))|(V&mask);\r
143       break;\r
144     }\r
145 \r
146     Sync();\r
147     break;\r
148   }\r
149   case 0xA000: regs[0xe] = (V & 1)^1; Sync(); break;\r
150   case 0xC000: IRQLatch = V&0xfe; break;\r
151   case 0xC001: IRQReload = 1; break;\r
152   case 0xE000: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break;\r
153   case 0xE001: IRQa = 1; break;\r
154   }\r
155 }\r
156 \r
157 static void UNLOneBusIRQHook(void)\r
158 {\r
159  int count = IRQCount;\r
160  if(!count || IRQReload)\r
161  {\r
162     IRQCount = IRQLatch;\r
163     IRQReload = 0;\r
164  }\r
165  else\r
166     IRQCount--;\r
167  if(count && !IRQCount)\r
168  {\r
169     if(IRQa)\r
170        X6502_IRQBegin(FCEU_IQEXT);\r
171  }\r
172 }\r
173 \r
174 static DECLFW(UNLOneBusWriteAPU2)\r
175 {\r
176 //  FCEU_printf("APU2 %04x:%04x\n",A,V);\r
177    CartBW(A&0xffdf,V);\r
178 }\r
179 \r
180 static DECLFW(UNLOneBusWrite4012)\r
181 {\r
182 //  FCEU_printf("write %04x:%04x\n",A,V);\r
183   pcm_addr = V << 6;\r
184   old4012write(A,V);\r
185 }\r
186 \r
187 static DECLFW(UNLOneBusWrite4013)\r
188 {\r
189 //  FCEU_printf("write %04x:%04x\n",A,V);\r
190   pcm_size = (V << 4) + 1;\r
191   old4013write(A,V);\r
192 }\r
193 \r
194 static DECLFW(UNLOneBusWrite4015)\r
195 {\r
196 //  FCEU_printf("write %04x:%04x\n",A,V);\r
197   pcm_enable = V&0x10;\r
198   if(pcm_irq)\r
199   {\r
200     X6502_IRQEnd(FCEU_IQEXT);\r
201     pcm_irq = 0;\r
202   }\r
203   if(pcm_enable)\r
204     pcm_latch = pcm_clock;\r
205   old4015write(A,V&0xEF);\r
206 }\r
207 \r
208 static DECLFR(UNLOneBusRead4015)\r
209 {\r
210   uint8 result = (old4015read(A) & 0x7F)|pcm_irq;\r
211 //  FCEU_printf("read %04x, %02x\n",A,result);\r
212   return result;\r
213 }\r
214 \r
215 static void UNLOneBusCpuHook(int a)\r
216 {\r
217   if(pcm_enable)\r
218   {\r
219     pcm_latch-=a;\r
220     if(pcm_latch<=0)\r
221     {\r
222           pcm_latch+=pcm_clock;\r
223           pcm_size--;\r
224           if(pcm_size<0)\r
225           {\r
226             pcm_irq = 0x80;\r
227                 pcm_enable = 0;\r
228             X6502_IRQBegin(FCEU_IQEXT);\r
229           }\r
230           else\r
231           {\r
232             uint8 raw_pcm = ARead[pcm_addr](pcm_addr) >> 1;\r
233             old4011write(0x4011,raw_pcm);\r
234                 pcm_addr++;\r
235                 pcm_addr&=0x7FFF;\r
236           }\r
237     }\r
238   }\r
239 }\r
240 \r
241 static void UNLOneBusPower(void)\r
242 {\r
243   IRQCount=IRQLatch=IRQa==0;\r
244   regs[0]=regs[1]=regs[1]=regs[2]=regs[3]=regs[4]=regs[5]=regs[6]=0;\r
245   regs[7]=regs[8]=regs[11]=regs[12]=regs[13]=regs[14]=regs[15]=0;\r
246   regs[0x09]=0x3E;\r
247   regs[0x0A]=0x3F;\r
248 \r
249   SetupCartCHRMapping(0,PRGptr[0],4096 * 1024,0);\r
250 \r
251   if(isDance) // quick workaround, TODO: figure out how it works together\r
252   {\r
253     old4015read=GetReadHandler(0x4015);\r
254     SetReadHandler(0x4015,0x4015,UNLOneBusRead4015);\r
255     old4011write=GetWriteHandler(0x4011);\r
256     old4012write=GetWriteHandler(0x4012);\r
257     SetWriteHandler(0x4012,0x4012,UNLOneBusWrite4012);\r
258     old4013write=GetWriteHandler(0x4013);\r
259     SetWriteHandler(0x4013,0x4013,UNLOneBusWrite4013);\r
260     old4015write=GetWriteHandler(0x4015);\r
261     SetWriteHandler(0x4015,0x4015,UNLOneBusWrite4015);\r
262   }\r
263 \r
264   SetReadHandler(0x8000,0xFFFF,CartBR);\r
265   SetWriteHandler(0x2009,0x2fff,UNLOneBusWrite20XX);\r
266 //  SetWriteHandler(0x4020,0xffff,UNLOneBusWriteDebug);\r
267 //  SetWriteHandler(0x4020,0x4040,UNLOneBusWriteAPU2);\r
268   SetWriteHandler(0x4100,0x410f,UNLOneBusWriteExp);\r
269   SetWriteHandler(0x8000,0xefff,UNLOneBusWriteMMC);\r
270   Sync();\r
271 }\r
272 \r
273 static void UNLOneBusReset(void)\r
274 {\r
275   IRQCount=IRQLatch=IRQa=0;\r
276   regs[0]=regs[1]=regs[1]=regs[2]=regs[3]=regs[4]=regs[5]=regs[6]=0;\r
277   regs[7]=regs[8]=regs[11]=regs[12]=regs[13]=regs[14]=regs[15]=0;\r
278   regs[0x09]=0x3E;\r
279   regs[0x0A]=0x3F;\r
280   Sync();\r
281 }\r
282 \r
283 static void StateRestore(int version)\r
284 {\r
285   Sync();\r
286 }\r
287 \r
288 void UNLOneBus_Init(CartInfo *info)\r
289 {\r
290   isDance = 0;\r
291   info->Power=UNLOneBusPower;\r
292   info->Reset=UNLOneBusReset;\r
293   GameHBIRQHook=UNLOneBusIRQHook;\r
294 //  MapIRQHook=UNLOneBusCpuHook;\r
295   GameStateRestore=StateRestore;\r
296   AddExState(&StateRegs, ~0, 0, 0);\r
297 }\r
298 \r
299 void UNLDANCE_Init(CartInfo *info)\r
300 {\r
301   isDance = 1;\r
302   info->Power=UNLOneBusPower;\r
303   info->Reset=UNLOneBusReset;\r
304   GameHBIRQHook=UNLOneBusIRQHook;\r
305   MapIRQHook=UNLOneBusCpuHook;\r
306   GameStateRestore=StateRestore;\r
307   AddExState(&StateRegs, ~0, 0, 0);\r
308 }\r