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