1 // FB Alpha Knuckle Bash 2 driver module
2 // Driver and emulation by Jan Klaassen
3
4 #include "toaplan.h"
5 // Knuckle Bash 2
6
7 static UINT8 DrvButton[8] = {0, 0, 0, 0, 0, 0, 0, 0};
8 static UINT8 DrvJoy1[8] = {0, 0, 0, 0, 0, 0, 0, 0};
9 static UINT8 DrvJoy2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
10 static UINT8 DrvInput[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
11
12 static UINT8 DrvReset = 0;
13
14 static INT32 nPreviousOkiBank;
15
16 static struct BurnRomInfo kbash2RomDesc[] = {
17 { "mecat-m", 0x080000, 0xbd2263c6, BRF_ESS | BRF_PRG }, // 0 CPU #0 code
18
19 { "mecat-34", 0x400000, 0x6be7b37e, BRF_GRA }, // 1 GP9001 Tile data
20 { "mecat-12", 0x400000, 0x49e46b1f, BRF_GRA }, // 2
21
22 { "mecat-s", 0x080000, 0x3eb7adf4, BRF_SND }, // 3 oki1
23
24 { "eprom", 0x040000, 0x31115cb9, BRF_SND }, // 4 oki2
25
26 { "050917-10", 0x010000, 0x6b213183, BRF_OPT }, // 5 WTF is this?
27 };
28
29 STD_ROM_PICK(kbash2)
30 STD_ROM_FN(kbash2)
31
32 static struct BurnInputInfo Kbash2InputList[] = {
33 {"P1 Coin", BIT_DIGITAL, DrvButton + 3, "p1 coin"},
34 {"P1 Start", BIT_DIGITAL, DrvButton + 5, "p1 start"},
35 {"P1 Up", BIT_DIGITAL, DrvJoy1 + 0, "p1 up"},
36 {"P1 Down", BIT_DIGITAL, DrvJoy1 + 1, "p1 down"},
37 {"P1 Left", BIT_DIGITAL, DrvJoy1 + 2, "p1 left"},
38 {"P1 Right", BIT_DIGITAL, DrvJoy1 + 3, "p1 right"},
39 {"P1 Button 1", BIT_DIGITAL, DrvJoy1 + 4, "p1 fire 1"},
40 {"P1 Button 2", BIT_DIGITAL, DrvJoy1 + 5, "p1 fire 2"},
41 {"P1 Button 3", BIT_DIGITAL, DrvJoy1 + 6, "p1 fire 3"},
42
43 {"P2 Coin", BIT_DIGITAL, DrvButton + 4, "p2 coin"},
44 {"P2 Start", BIT_DIGITAL, DrvButton + 6, "p2 start"},
45 {"P2 Up", BIT_DIGITAL, DrvJoy2 + 0, "p2 up"},
46 {"P2 Down", BIT_DIGITAL, DrvJoy2 + 1, "p2 down"},
47 {"P2 Left", BIT_DIGITAL, DrvJoy2 + 2, "p2 left"},
48 {"P2 Right", BIT_DIGITAL, DrvJoy2 + 3, "p2 right"},
49 {"P2 Button 1", BIT_DIGITAL, DrvJoy2 + 4, "p2 fire 1"},
50 {"P2 Button 2", BIT_DIGITAL, DrvJoy2 + 5, "p2 fire 2"},
51 {"P2 Button 3", BIT_DIGITAL, DrvJoy2 + 6, "p2 fire 3"},
52
53 {"Reset", BIT_DIGITAL, &DrvReset, "reset"},
54 {"Service", BIT_DIGITAL, DrvButton + 0, "service"},
55 {"Tilt", BIT_DIGITAL, DrvButton + 1, "tilt"},
56 {"Dip A", BIT_DIPSWITCH, DrvInput + 3, "dip"},
57 {"Dip B", BIT_DIPSWITCH, DrvInput + 4, "dip"},
58 {"Dip C", BIT_DIPSWITCH, DrvInput + 5, "dip"},
59 };
60
61 STDINPUTINFO(Kbash2)
62
63 static struct BurnDIPInfo Kbash2DIPList[]=
64 {
65 {0x15, 0xff, 0xff, 0x00, NULL },
66 {0x16, 0xff, 0xff, 0x00, NULL },
67 {0x17, 0xff, 0xff, 0x06, NULL },
68
69 {0 , 0xfe, 0 , 2, "Continue Mode" },
70 {0x15, 0x01, 0x01, 0x00, "Normal" },
71 {0x15, 0x01, 0x01, 0x01, "Discount" },
72
73 {0 , 0xfe, 0 , 2, "Flip Screen" },
74 {0x15, 0x01, 0x02, 0x00, "Off" },
75 {0x15, 0x01, 0x02, 0x02, "On" },
76
77 {0 , 0xfe, 0 , 2, "Service Mode" },
78 {0x15, 0x01, 0x04, 0x00, "Off" },
79 {0x15, 0x01, 0x04, 0x04, "On" },
80
81 {0 , 0xfe, 0 , 2, "Demo Sounds" },
82 {0x15, 0x01, 0x08, 0x08, "Off" },
83 {0x15, 0x01, 0x08, 0x00, "On" },
84
85 {0 , 0xfe, 0 , 7, "Coin A" },
86 {0x15, 0x01, 0x30, 0x30, "4 Coins 1 Credits" },
87 {0x15, 0x01, 0x30, 0x20, "3 Coins 1 Credits" },
88 {0x15, 0x01, 0x30, 0x10, "2 Coins 1 Credits" },
89 {0x15, 0x01, 0x30, 0x20, "2 Coins 1 Credits" },
90 {0x15, 0x01, 0x30, 0x00, "1 Coin 1 Credits" },
91 {0x15, 0x01, 0x30, 0x30, "2 Coins 3 Credits" },
92 {0x15, 0x01, 0x30, 0x10, "1 Coin 2 Credits" },
93
94 {0 , 0xfe, 0 , 8, "Coin B" },
95 {0x15, 0x01, 0xc0, 0x80, "2 Coins 1 Credits" },
96 {0x15, 0x01, 0xc0, 0x00, "1 Coin 1 Credits" },
97 {0x15, 0x01, 0xc0, 0xc0, "2 Coins 3 Credits" },
98 {0x15, 0x01, 0xc0, 0x40, "1 Coin 2 Credits" },
99 {0x15, 0x01, 0xc0, 0x00, "1 Coin 2 Credits" },
100 {0x15, 0x01, 0xc0, 0x40, "1 Coin 3 Credits" },
101 {0x15, 0x01, 0xc0, 0x80, "1 Coin 4 Credits" },
102 {0x15, 0x01, 0xc0, 0xc0, "1 Coin 6 Credits" },
103
104 {0 , 0xfe, 0 , 4, "Difficulty" },
105 {0x16, 0x01, 0x03, 0x03, "Hardest" },
106 {0x16, 0x01, 0x03, 0x02, "Hard" },
107 {0x16, 0x01, 0x03, 0x00, "Medium" },
108 {0x16, 0x01, 0x03, 0x01, "Easy" },
109
110 {0 , 0xfe, 0 , 4, "Bonus Life" },
111 {0x16, 0x01, 0x0c, 0x0c, "None" },
112 {0x16, 0x01, 0x0c, 0x08, "200k only" },
113 {0x16, 0x01, 0x0c, 0x04, "100k only" },
114 {0x16, 0x01, 0x0c, 0x00, "100k and 400k" },
115
116 {0 , 0xfe, 0 , 4, "Lives" },
117 {0x16, 0x01, 0x30, 0x30, "1" },
118 {0x16, 0x01, 0x30, 0x00, "2" },
119 {0x16, 0x01, 0x30, 0x20, "3" },
120 {0x16, 0x01, 0x30, 0x10, "4" },
121
122 {0 , 0xfe, 0 , 2, "Invulnerability" },
123 {0x16, 0x01, 0x40, 0x00, "Off" },
124 {0x16, 0x01, 0x40, 0x40, "On" },
125
126 {0 , 0xfe, 0 , 2, "Allow Continue" },
127 {0x16, 0x01, 0x80, 0x80, "No" },
128 {0x16, 0x01, 0x80, 0x00, "Yes" },
129
130 {0 , 0xfe, 0 , 7, "Territory" },
131 {0x17, 0x01, 0x0f, 0x00, "Japan (Taito Corp license)" },
132 {0x17, 0x01, 0x0f, 0x0e, "South East Asia" },
133 {0x17, 0x01, 0x0f, 0x06, "South East Asia (Charterfield license)" },
134 {0x17, 0x01, 0x0f, 0x0b, "Korea" },
135 {0x17, 0x01, 0x0f, 0x03, "Korea (Unite license)" },
136 {0x17, 0x01, 0x0f, 0x04, "Hong Kong" },
137 {0x17, 0x01, 0x0f, 0x05, "Taiwan" },
138 };
139
140 STDDIPINFO(Kbash2)
141
142 static UINT8 *Mem = NULL, *MemEnd = NULL;
143 static UINT8 *RamStart, *RamEnd;
144 static UINT8 *Rom01;
145 static UINT8 *Ram01, *RamPal, *RamSnd;
146 static UINT8 *RomSnd;
147
148 static INT32 nColCount = 0x0800;
149
150 // This routine is called first to determine how much memory is needed (MemEnd-(UINT8 *)0),
151 // and then afterwards to set up all the pointers
MemIndex()152 static INT32 MemIndex()
153 {
154 UINT8 *Next; Next = Mem;
155 Rom01 = Next; Next += 0x080000; // 68000 ROM
156 MSM6295ROM = Next;
157 RomSnd = Next; Next += 0x140000;
158 GP9001ROM[0]= Next; Next += nGP9001ROMSize[0]; // GP9001 tile data
159 RamStart = Next;
160 Ram01 = Next; Next += 0x004000; // CPU #0 work RAM
161 RamPal = Next; Next += 0x001000; // palette
162 RamSnd = Next; Next += 0x000100; // sound work-ram
163 GP9001RAM[0]= Next; Next += 0x004000;
164 GP9001Reg[0]= (UINT16*)Next; Next += 0x0100 * sizeof(UINT16);
165 RamEnd = Next;
166 ToaPalette = (UINT32 *)Next; Next += nColCount * sizeof(UINT32);
167 MemEnd = Next;
168
169 return 0;
170 }
171
oki_set_bank(INT32 bank)172 static void oki_set_bank(INT32 bank)
173 {
174 bank &= 1;
175 if (nPreviousOkiBank != bank) {
176 nPreviousOkiBank = bank;
177 memcpy (RomSnd + 0x000000, RomSnd + 0x40000 + (bank * 0x40000), 0x40000);
178 }
179 }
180
181 // Scan ram
DrvScan(INT32 nAction,INT32 * pnMin)182 static INT32 DrvScan(INT32 nAction,INT32 *pnMin)
183 {
184 struct BurnArea ba;
185
186 if (pnMin) { // Return minimum compatible version
187 *pnMin = 0x020997;
188 }
189 if (nAction & ACB_VOLATILE) { // Scan volatile ram
190 memset(&ba, 0, sizeof(ba));
191 ba.Data = RamStart;
192 ba.nLen = RamEnd-RamStart;
193 ba.szName = "All Ram";
194 BurnAcb(&ba);
195
196 SekScan(nAction); // scan 68000 states
197
198 MSM6295Scan(nAction, pnMin);
199
200 ToaScanGP9001(nAction, pnMin);
201
202 SCAN_VAR(nPreviousOkiBank);
203 }
204
205 if (nAction & ACB_WRITE) {
206 INT32 nBank = nPreviousOkiBank;
207 nPreviousOkiBank = -1;
208 oki_set_bank(nBank);
209 }
210
211 return 0;
212 }
213
LoadRoms()214 static INT32 LoadRoms()
215 {
216 // Load 68000 ROM
217 BurnLoadRom(Rom01, 0, 1);
218
219 // Load GP9001 tile data
220 ToaLoadGP9001Tiles(GP9001ROM[0], 1, 2, nGP9001ROMSize[0]);
221
222 if (BurnLoadRom(RomSnd + 0x040000, 3, 1)) return 1;
223 if (BurnLoadRom(RomSnd + 0x100000, 4, 1)) return 1;
224
225 return 0;
226 }
227
kbash2ReadByte(UINT32 sekAddress)228 static UINT8 __fastcall kbash2ReadByte(UINT32 sekAddress)
229 {
230 switch (sekAddress) {
231
232 case 0x200005: // Dipswitch 1
233 return DrvInput[3];
234 case 0x200009: // Dipswitch 2
235 return DrvInput[4];
236 case 0x20000d: // Dipswitch 3 - Territory
237 return DrvInput[5];
238
239 case 0x200011: // Player 1 Input
240 return DrvInput[0];
241 case 0x200015: // Player 2 Input
242 return DrvInput[1];
243 case 0x200019: // System...
244 return DrvInput[2];
245
246 case 0x200021:
247 return MSM6295Read(1);
248
249 case 0x200025:
250 return MSM6295Read(0);
251
252 case 0x20002D:
253 return ToaScanlineRegister();
254
255 case 0x30000d: // VBlank
256 return ToaVBlankRegister();
257
258 // default:
259 // printf("Attempt to read byte value of location %x\n", sekAddress);
260 }
261
262 return 0;
263 }
264
kbash2ReadWord(UINT32 sekAddress)265 static UINT16 __fastcall kbash2ReadWord(UINT32 sekAddress)
266 {
267 switch (sekAddress) {
268
269 case 0x200004: // Dipswitch 1
270 return DrvInput[3];
271 case 0x200008: // Dipswitch 2
272 return DrvInput[4];
273 case 0x20000c: // Dipswitch 3 - Territory
274 return DrvInput[5];
275
276 case 0x200010: // Player 1 Input
277 return DrvInput[0];
278 case 0x200014: // Player 2 Input
279 return DrvInput[1];
280 case 0x200018: // System...
281 return DrvInput[2];
282
283 case 0x200020:
284 return MSM6295Read(1);
285
286 case 0x200024:
287 return MSM6295Read(0);
288
289 case 0x20002c:
290 return ToaScanlineRegister();
291
292 case 0x300004:
293 return ToaGP9001ReadRAM_Hi(0);
294 case 0x300006:
295 return ToaGP9001ReadRAM_Lo(0);
296
297 case 0x30000C: // VBlank
298 return ToaVBlankRegister();
299
300 // default:
301 // printf("Attempt to read word value of location %x\n", sekAddress);
302 }
303
304 return 0;
305 }
306
kbash2WriteByte(UINT32 sekAddress,UINT8 byteValue)307 static void __fastcall kbash2WriteByte(UINT32 sekAddress, UINT8 byteValue)
308 {
309 switch (sekAddress) {
310 case 0x200021:
311 MSM6295Write(1, byteValue);
312 return;
313
314 case 0x200025:
315 MSM6295Write(0, byteValue);
316 return;
317
318 case 0x200029:
319 oki_set_bank(byteValue);
320 return;
321
322 // default:
323 // printf("Attempt to write byte value %x to location %x\n", byteValue, sekAddress);
324 }
325 }
326
kbash2WriteWord(UINT32 sekAddress,UINT16 wordValue)327 static void __fastcall kbash2WriteWord(UINT32 sekAddress, UINT16 wordValue)
328 {
329 switch (sekAddress) {
330 case 0x300000: // Set GP9001 VRAM address-pointer
331 ToaGP9001SetRAMPointer(wordValue);
332 break;
333
334 case 0x300004:
335 ToaGP9001WriteRAM(wordValue, 0);
336 break;
337 case 0x300006:
338 ToaGP9001WriteRAM(wordValue, 0);
339 break;
340
341 case 0x300008:
342 ToaGP9001SelectRegister(wordValue);
343 break;
344
345 case 0x30000C:
346 ToaGP9001WriteRegister(wordValue);
347 break;
348
349 // default:
350 // printf("Attempt to write word value %x to location %x\n", wordValue, sekAddress);
351 }
352 }
353
DrvDoReset()354 static INT32 DrvDoReset()
355 {
356 SekOpen(0);
357 SekReset();
358 SekClose();
359
360 MSM6295Reset();
361
362 nPreviousOkiBank = -1;
363 oki_set_bank(0);
364
365 return 0;
366 }
367
DrvInit()368 static INT32 DrvInit()
369 {
370 INT32 nLen;
371
372 #ifdef DRIVER_ROTATION
373 bToaRotateScreen = false;
374 #endif
375
376 nGP9001ROMSize[0] = 0x800000;
377
378 // Find out how much memory is needed
379 Mem = NULL;
380 MemIndex();
381 nLen = MemEnd - (UINT8 *)0;
382 if ((Mem = (UINT8 *)BurnMalloc(nLen)) == NULL) {
383 return 1;
384 }
385 memset(Mem, 0, nLen); // blank all memory
386 MemIndex(); // Index the allocated memory
387
388 // Load the roms into memory
389 if (LoadRoms()) {
390 return 1;
391 }
392
393 {
394 SekInit(0, 0x68000); // Allocate 68000
395 SekOpen(0);
396 SekMapMemory(Rom01, 0x000000, 0x07FFFF, MAP_ROM);
397 SekMapMemory(Ram01, 0x100000, 0x103FFF, MAP_RAM);
398 SekMapMemory(RamSnd, 0x104000, 0x1040FF, MAP_RAM);
399 SekMapMemory(RamPal, 0x400000, 0x400FFF, MAP_RAM);
400 SekSetReadWordHandler(0, kbash2ReadWord);
401 SekSetReadByteHandler(0, kbash2ReadByte);
402 SekSetWriteWordHandler(0, kbash2WriteWord);
403 SekSetWriteByteHandler(0, kbash2WriteByte);
404
405 SekClose();
406 }
407
408 MSM6295Init(0, 1000000 / 132, 0);
409 MSM6295Init(1, 1000000 / 132, 1);
410 MSM6295SetRoute(0, 0.70, BURN_SND_ROUTE_BOTH);
411 MSM6295SetRoute(1, 0.70, BURN_SND_ROUTE_BOTH);
412
413 nSpriteYOffset = 0x0011;
414
415 nLayer0XOffset = -0x01D6;
416 nLayer1XOffset = -0x01D8;
417 nLayer2XOffset = -0x01DA;
418
419 ToaInitGP9001();
420
421 nToaPalLen = nColCount;
422 ToaPalSrc = RamPal;
423 ToaPalInit();
424
425 DrvDoReset(); // Reset machine
426 return 0;
427 }
428
DrvExit()429 static INT32 DrvExit()
430 {
431 ToaPalExit();
432
433 ToaExitGP9001();
434 SekExit(); // Deallocate 68000s
435
436 MSM6295Exit();
437
438 BurnFree(Mem);
439
440 return 0;
441 }
442
DrvDraw()443 static INT32 DrvDraw()
444 {
445 ToaClearScreen(0);
446
447 ToaGetBitmap();
448 ToaRenderGP9001(); // Render GP9001 graphics
449
450 ToaPalUpdate(); // Update the palette
451
452 return 0;
453 }
454
DrvFrame()455 static INT32 DrvFrame()
456 {
457 INT32 nInterleave = 4;
458
459 if (DrvReset) { // Reset machine
460 DrvDoReset();
461 }
462
463 // Compile digital inputs
464 DrvInput[0] = 0x00; // Buttons
465 DrvInput[1] = 0x00; // Player 1
466 DrvInput[2] = 0x00; // Player 2
467 for (INT32 i = 0; i < 8; i++) {
468 DrvInput[0] |= (DrvJoy1[i] & 1) << i;
469 DrvInput[1] |= (DrvJoy2[i] & 1) << i;
470 DrvInput[2] |= (DrvButton[i] & 1) << i;
471 }
472 ToaClearOpposites(&DrvInput[0]);
473 ToaClearOpposites(&DrvInput[1]);
474
475 SekNewFrame();
476
477 nCyclesTotal[0] = (INT32)((INT64)16000000 * nBurnCPUSpeedAdjust / (0x0100 * 60));
478 nCyclesDone[0] = 0;
479
480 SekOpen(0);
481
482 SekSetCyclesScanline(nCyclesTotal[0] / 262);
483 nToaCyclesDisplayStart = nCyclesTotal[0] - ((nCyclesTotal[0] * (TOA_VBLANK_LINES + 240)) / 262);
484 nToaCyclesVBlankStart = nCyclesTotal[0] - ((nCyclesTotal[0] * TOA_VBLANK_LINES) / 262);
485 bool bVBlank = false;
486
487 for (INT32 i = 0; i < nInterleave; i++) {
488 INT32 nCurrentCPU;
489 INT32 nNext;
490
491 // Run 68000
492 nCurrentCPU = 0;
493 nNext = (i + 1) * nCyclesTotal[nCurrentCPU] / nInterleave;
494
495 // Trigger VBlank interrupt
496 if (!bVBlank && nNext > nToaCyclesVBlankStart) {
497 if (nCyclesDone[nCurrentCPU] < nToaCyclesVBlankStart) {
498 nCyclesSegment = nToaCyclesVBlankStart - nCyclesDone[nCurrentCPU];
499 nCyclesDone[nCurrentCPU] += SekRun(nCyclesSegment);
500 }
501
502 bVBlank = true;
503
504 ToaBufferGP9001Sprites();
505
506 // Trigger VBlank interrupt
507 SekSetIRQLine(4, CPU_IRQSTATUS_AUTO);
508 }
509
510 nCyclesSegment = nNext - nCyclesDone[nCurrentCPU];
511 nCyclesDone[nCurrentCPU] += SekRun(nCyclesSegment);
512 }
513
514 if (pBurnSoundOut) {
515 MSM6295Render(pBurnSoundOut, nBurnSoundLen);
516 }
517
518 SekClose();
519
520 if (pBurnDraw) {
521 DrvDraw(); // Draw screen if needed
522 }
523
524 return 0;
525 }
526
527 struct BurnDriver BurnDrvKbash2 = {
528 "kbash2", NULL, NULL, NULL, "1999",
529 "Knuckle Bash 2 (bootleg)\0", NULL, "bootleg", "Toaplan GP9001 based",
530 L"Knuckle Bash 2\0Knuckle Bash \u30CA\u30C3\u30AF\u30EB\u30D0\u30C3\u30B7\u30E5 \uFF12 (bootleg)\0", NULL, NULL, NULL,
531 BDF_GAME_WORKING | BDF_BOOTLEG, 2, HARDWARE_TOAPLAN_68K_ONLY, GBF_SCRFIGHT, 0,
532 NULL, kbash2RomInfo, kbash2RomName, NULL, NULL, NULL, NULL, Kbash2InputInfo, Kbash2DIPInfo,
533 DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &ToaRecalcPalette, 0x800,
534 320, 240, 4, 3
535 };
536