merge mappers from FCEU-mm
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  *\r
20  * VR02/VT03 Console and OneBus System\r
21  *\r
22  * Street Dance (Dance pad) (Unl)\r
23  * 101-in-1 Arcade Action II\r
24  * DreamGEAR 75-in-1, etc.\r
25  *\r
26  */\r
27 \r
28 #include "mapinc.h"\r
29 \r
30 // General Purpose Registers\r
31 static uint8 cpu410x[16], ppu201x[16], apu40xx[64];\r
32 \r
33 // IRQ Registers\r
34 static uint8 IRQCount, IRQa, IRQReload;\r
35 #define IRQLatch cpu410x[0x1]\r
36 \r
37 // MMC3 Registers\r
38 static uint8 inv_hack = 0; // some OneBus Systems have swapped PRG reg commans in MMC3 inplementation,\r
39                            // trying to autodetect unusual behavior, due not to add a new mapper.\r
40 #define mmc3cmd  cpu410x[0x5]\r
41 #define mirror   cpu410x[0x6]\r
42 \r
43 // APU Registers\r
44 static uint8 pcm_enable = 0, pcm_irq = 0;\r
45 static int16 pcm_addr, pcm_size, pcm_latch, pcm_clock = 0xF6;\r
46 \r
47 static writefunc defapuwrite[64];\r
48 static readfunc defapuread[64];\r
49 \r
50 static SFORMAT StateRegs[]=\r
51 {\r
52   {cpu410x, 16, "REGC"},\r
53   {ppu201x, 16, "REGS"},\r
54   {apu40xx, 64, "REGA"},\r
55   {&IRQReload, 1, "IRQR"},\r
56   {&IRQCount, 1, "IRQC"},\r
57   {&IRQa, 1, "IRQA"},\r
58   {&pcm_enable, 1, "PCME"},\r
59   {&pcm_irq, 1, "PCMI"},\r
60   {&pcm_addr, 2, "PCMA"},\r
61   {&pcm_size, 2, "PCMS"},\r
62   {&pcm_latch, 2, "PCML"},\r
63   {&pcm_clock, 2, "PCMC"},\r
64   {0}\r
65 };\r
66 \r
67 static void PSync(void)\r
68 {\r
69   uint8  bankmode = cpu410x[0xb] & 7;\r
70   uint8  mask  = (bankmode == 0x7)?(0xff):(0x3f >> bankmode);\r
71   uint32 block = ((cpu410x[0x0] & 0xf0) << 4) + (cpu410x[0xa] & (~mask));\r
72   uint32 pswap = (mmc3cmd & 0x40) << 8;\r
73 \r
74 //  uint8 bank0  = (cpu410x[0xb] & 0x40)?(~1):(cpu410x[0x7]);\r
75 //  uint8 bank1  = cpu410x[0x8];\r
76 //  uint8 bank2  = (cpu410x[0xb] & 0x40)?(cpu410x[0x9]):(~1);\r
77 //  uint8 bank3  = ~0;\r
78   uint8 bank0  = cpu410x[0x7^inv_hack];\r
79   uint8 bank1  = cpu410x[0x8^inv_hack];\r
80   uint8 bank2  = (cpu410x[0xb] & 0x40)?(cpu410x[0x9]):(~1);\r
81   uint8 bank3  = ~0;\r
82 \r
83 //  FCEU_printf(" PRG: %04x [%02x]",0x8000^pswap,block | (bank0 & mask));\r
84   setprg8(0x8000^pswap, block | (bank0 & mask));\r
85 //  FCEU_printf(" %04x [%02x]",0xa000^pswap,block | (bank1 & mask));\r
86   setprg8(0xa000,       block | (bank1 & mask));\r
87 //  FCEU_printf(" %04x [%02x]",0xc000^pswap,block | (bank2 & mask));\r
88   setprg8(0xc000^pswap, block | (bank2 & mask));\r
89 //  FCEU_printf(" %04x [%02x]\n",0xe000^pswap,block | (bank3 & mask));\r
90   setprg8(0xe000,       block | (bank3 & mask));\r
91 }\r
92 \r
93 static void CSync(void)\r
94 {\r
95   static const uint8 midx[8] = {0, 1, 2, 0, 3, 4, 5, 0 };\r
96   uint8  mask  = 0xff >> midx[ppu201x[0xa] & 7];\r
97   uint32 block = ((cpu410x[0x0] & 0x0f) << 11) + ((ppu201x[0x8] & 0x70) << 4) + (ppu201x[0xa] & (~mask));\r
98   uint32 cswap = (mmc3cmd & 0x80) << 5;\r
99 \r
100   uint8 bank0  = ppu201x[0x6]&(~1);\r
101   uint8 bank1  = ppu201x[0x6]|1;\r
102   uint8 bank2  = ppu201x[0x7]&(~1);\r
103   uint8 bank3  = ppu201x[0x7]|1;\r
104   uint8 bank4  = ppu201x[0x2];\r
105   uint8 bank5  = ppu201x[0x3];\r
106   uint8 bank6  = ppu201x[0x4];\r
107   uint8 bank7  = ppu201x[0x5];\r
108 \r
109   setchr1(0x0000^cswap, block | (bank0 & mask));\r
110   setchr1(0x0400^cswap, block | (bank1 & mask));\r
111   setchr1(0x0800^cswap, block | (bank2 & mask));\r
112   setchr1(0x0c00^cswap, block | (bank3 & mask));\r
113   setchr1(0x1000^cswap, block | (bank4 & mask));\r
114   setchr1(0x1400^cswap, block | (bank5 & mask));\r
115   setchr1(0x1800^cswap, block | (bank6 & mask));\r
116   setchr1(0x1c00^cswap, block | (bank7 & mask));\r
117 \r
118   setmirror((mirror & 1) ^ 1);\r
119 }\r
120 \r
121 static void Sync(void)\r
122 {\r
123   PSync();\r
124   CSync();\r
125 }\r
126 \r
127 static DECLFW(UNLOneBusWriteCPU410X)\r
128 {\r
129 //  FCEU_printf("CPU %04x:%04x\n",A,V);\r
130   switch(A & 0xf)\r
131   {\r
132   case 0x1: IRQLatch = V & 0xfe; break;\r
133   case 0x2: IRQReload = 1; break;\r
134   case 0x3: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break;\r
135   case 0x4: IRQa = 1; break;\r
136   default:\r
137     cpu410x[A & 0xf] = V;\r
138     Sync();\r
139   }\r
140 }\r
141 \r
142 static DECLFW(UNLOneBusWritePPU201X)\r
143 {\r
144 //  FCEU_printf("PPU %04x:%04x\n",A,V);\r
145   ppu201x[A & 0x0f] = V;\r
146   Sync();\r
147 }\r
148 \r
149 static DECLFW(UNLOneBusWriteMMC3)\r
150 {\r
151 //  FCEU_printf("MMC %04x:%04x\n",A,V);\r
152   switch(A&0xe001)\r
153   {\r
154   case 0x8000: mmc3cmd = (mmc3cmd & 0x38) | (V & 0xc7); Sync(); break;\r
155   case 0x8001:\r
156   {\r
157     switch(mmc3cmd & 7)\r
158     {\r
159     case 0: ppu201x[0x6] = V; CSync(); break;\r
160     case 1: ppu201x[0x7] = V; CSync(); break;\r
161     case 2: ppu201x[0x2] = V; CSync(); break;\r
162     case 3: ppu201x[0x3] = V; CSync(); break;\r
163     case 4: ppu201x[0x4] = V; CSync(); break;\r
164     case 5: ppu201x[0x5] = V; CSync(); break;\r
165     case 6: cpu410x[0x7] = V; PSync(); break;\r
166     case 7: cpu410x[0x8] = V; PSync(); break;\r
167     }\r
168     break;\r
169   }\r
170   case 0xa000: mirror = V; CSync(); break;\r
171   case 0xc000: IRQLatch = V & 0xfe; break;\r
172   case 0xc001: IRQReload = 1; break;\r
173   case 0xe000: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break;\r
174   case 0xe001: IRQa = 1; break;\r
175   }\r
176 }\r
177 \r
178 static void UNLOneBusIRQHook(void)\r
179 {\r
180  int count = IRQCount;\r
181  if(!count || IRQReload)\r
182  {\r
183     IRQCount = IRQLatch;\r
184     IRQReload = 0;\r
185  }\r
186  else\r
187     IRQCount--;\r
188  if(count && !IRQCount)\r
189  {\r
190     if(IRQa)\r
191        X6502_IRQBegin(FCEU_IQEXT);\r
192  }\r
193 }\r
194 \r
195 static DECLFW(UNLOneBusWriteAPU40XX)\r
196 {\r
197 //  FCEU_printf("APU %04x:%04x\n",A,V);\r
198   apu40xx[A & 0x3f] = V;\r
199   switch(A & 0x3f)\r
200   {\r
201   case 0x12:\r
202     if(apu40xx[0x30] & 0x10)\r
203     {\r
204       pcm_addr = V << 6;\r
205     }\r
206   case 0x13:\r
207     if(apu40xx[0x30] & 0x10)\r
208     {\r
209       pcm_size = (V << 4) + 1;\r
210     }\r
211   case 0x15:\r
212     if(apu40xx[0x30] & 0x10)\r
213     {\r
214       pcm_enable = V&0x10;\r
215       if(pcm_irq)\r
216       {\r
217         X6502_IRQEnd(FCEU_IQEXT);\r
218         pcm_irq = 0;\r
219       }\r
220       if(pcm_enable)\r
221         pcm_latch = pcm_clock;\r
222       V &= 0xef;\r
223     }\r
224   }\r
225   defapuwrite[A & 0x3f](A, V);\r
226 }\r
227 \r
228 static DECLFR(UNLOneBusReadAPU40XX)\r
229 {\r
230   uint8 result = defapuread[A & 0x3f](A);\r
231 //  FCEU_printf("read %04x, %02x\n",A,result);\r
232   switch(A & 0x3f)\r
233   {\r
234   case 0x15:\r
235     if(apu40xx[0x30] & 0x10)\r
236     {\r
237       result = (result & 0x7f) | pcm_irq;\r
238     }\r
239   }\r
240   return result;\r
241 }\r
242 \r
243 static void UNLOneBusCpuHook(int a)\r
244 {\r
245   if(pcm_enable)\r
246   {\r
247     pcm_latch-=a;\r
248     if(pcm_latch<=0)\r
249     {\r
250           pcm_latch+=pcm_clock;\r
251           pcm_size--;\r
252           if(pcm_size<0)\r
253           {\r
254             pcm_irq = 0x80;\r
255                 pcm_enable = 0;\r
256             X6502_IRQBegin(FCEU_IQEXT);\r
257           }\r
258           else\r
259           {\r
260             uint8 raw_pcm = ARead[pcm_addr](pcm_addr) >> 1;\r
261             defapuwrite[0x11](0x4011,raw_pcm);\r
262                 pcm_addr++;\r
263                 pcm_addr&=0x7FFF;\r
264           }\r
265     }\r
266   }\r
267 }\r
268 \r
269 static void UNLOneBusPower(void)\r
270 {\r
271   uint32 i;\r
272   IRQReload = IRQCount = IRQa = 0;\r
273 \r
274   memset(cpu410x, 0x00, sizeof(cpu410x));\r
275   memset(ppu201x, 0x00, sizeof(ppu201x));\r
276   memset(apu40xx, 0x00, sizeof(apu40xx));\r
277 \r
278   SetupCartCHRMapping(0, PRGptr[0], PRGsize[0], 0);\r
279 \r
280   for(i=0; i<64; i++)\r
281   {\r
282     defapuread[i] = GetReadHandler(0x4000|i);\r
283     defapuwrite[i] = GetWriteHandler(0x4000|i);\r
284   }\r
285   SetReadHandler(0x4000,0x403f,UNLOneBusReadAPU40XX);\r
286   SetWriteHandler(0x4000,0x403f,UNLOneBusWriteAPU40XX);\r
287 \r
288   SetReadHandler(0x8000,0xFFFF,CartBR);\r
289   SetWriteHandler(0x2010,0x201f,UNLOneBusWritePPU201X);\r
290   SetWriteHandler(0x4100,0x410f,UNLOneBusWriteCPU410X);\r
291   SetWriteHandler(0x8000,0xffff,UNLOneBusWriteMMC3);\r
292 \r
293   Sync();\r
294 }\r
295 \r
296 static void UNLOneBusReset(void)\r
297 {\r
298   IRQReload = IRQCount = IRQa = 0;\r
299 \r
300   memset(cpu410x, 0x00, sizeof(cpu410x));\r
301   memset(ppu201x, 0x00, sizeof(ppu201x));\r
302   memset(apu40xx, 0x00, sizeof(apu40xx));\r
303 \r
304   Sync();\r
305 }\r
306 \r
307 static void StateRestore(int version)\r
308 {\r
309   Sync();\r
310 }\r
311 \r
312 void UNLOneBus_Init(CartInfo *info)\r
313 {\r
314   info->Power=UNLOneBusPower;\r
315   info->Reset=UNLOneBusReset;\r
316 \r
317   if(((*(uint32*)&(info->MD5)) == 0x305fcdc3) || // PowerJoy Supermax Carts\r
318      ((*(uint32*)&(info->MD5)) == 0x6abfce8e) )\r
319     inv_hack = 0xf;\r
320 \r
321   GameHBIRQHook=UNLOneBusIRQHook;\r
322   MapIRQHook=UNLOneBusCpuHook;\r
323   GameStateRestore=StateRestore;\r
324   AddExState(&StateRegs, ~0, 0, 0);\r
325 }\r