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