1 /*  gngeo a neogeo emulator
2  *  Copyright (C) 2001 Peponas Mathieu
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU Library General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /* cyclone interface */
20 
21 
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #ifdef USE_CYCLONE
28 
29 #include <stdlib.h>
30 
31 #include "cyclone/Cyclone.h"
32 #include "memory.h"
33 #include "emu.h"
34 #include "state.h"
35 #include "debug.h"
36 #include "video.h"
37 #include "conf.h"
38 
39 struct Cyclone MyCyclone;
40 static int total_cycles;
41 static int time_slice;
42 Uint32 cyclone_pc;
43 extern int current_line;
44 Uint8 save_buffer[128];
45 
46 extern Uint32 irq2pos_value;
cyclone68k_store_video_word(Uint32 addr,Uint16 data)47 static __inline__ void cyclone68k_store_video_word(Uint32 addr, Uint16 data)
48 {
49 	//SDL_Swap16(data);
50 	//printf("mem68k_store_video_word %08x %04x\n",addr,data);
51 
52     addr &= 0xFFFF;
53     switch (addr) {
54     case 0x0:
55     	memory.vid.vptr = data & 0xffff;
56 	break;
57     case 0x2:
58 	//if (((vptr<<1)==0x10800+0x8) ) printf("Store to video %08x @pc=%08x\n",vptr<<1,cpu_68k_getpc());
59 	/*
60 	  if (((vptr<<1)==0x10000+0x17e) ||
61 	  ((vptr<<1)==0x10400+0x17e) ||
62 	  ((vptr<<1)==0x10800+0x17e) ) printf("Store to video %08x @pc=%08x\n",vptr<<1,cpu_68k_getpc());
63 	*/
64 	WRITE_WORD(&memory.vid.ram[memory.vid.vptr << 1], data);
65 	memory.vid.vptr = (memory.vid.vptr + memory.vid.modulo) & 0xffff;
66 	break;
67     case 0x4:
68     	memory.vid.modulo = (int) data;
69 	break;
70     case 0x6:
71 	write_neo_control(data);
72 	break;
73     case 0x8:
74 	write_irq2pos((memory.vid.irq2pos & 0xffff) | ((Uint32) data << 16));
75 	break;
76     case 0xa:
77 	write_irq2pos((memory.vid.irq2pos & 0xffff0000) | (Uint32) data);
78 	break;
79     case 0xc:
80 	/* games write 7 or 4 at 0x3c000c at every frame */
81 	/* IRQ acknowledge */
82 	break;
83     }
84 
85 
86 }
87 
print_one_reg(Uint32 r)88 static void print_one_reg(Uint32 r) {
89 	printf("reg=%08x\n",r);
90 }
91 
swap_memory(Uint8 * mem,Uint32 length)92 static void swap_memory(Uint8 * mem, Uint32 length)
93 {
94     int i, j;
95 
96     /* swap bytes in each word */
97     for (i = 0; i < length; i += 2) {
98 	j = mem[i];
99 	mem[i] = mem[i + 1];
100 	mem[i + 1] = j;
101     }
102 }
103 
MyCheckPc(unsigned int pc)104 static unsigned int   MyCheckPc(unsigned int pc) {
105 	int i;
106 	pc-=MyCyclone.membase; // Get the real program counter
107 
108 	pc&=0xFFFFFF;
109 	MyCyclone.membase=-1;
110 
111 /*	printf("## Check pc %08x\n",pc);
112 	for(i=0;i<8;i++) printf("  d%d=%08x a%d=%08x\n",i,MyCyclone.d[i],i,MyCyclone.a[i]); */
113 
114 	//printf("PC %08x %08x\n",(pc&0xF00000),(pc&0xF00000)>>20);
115 	switch((pc&0xF00000)>>20) {
116 	case 0x0:
117 		MyCyclone.membase=(int)memory.rom.cpu_m68k.p;
118 		break;
119 	case 0x2:
120 		MyCyclone.membase=(int)(memory.rom.cpu_m68k.p+bankaddress)-0x200000;
121 		break;
122 	case 0x1:
123 		if (pc<=0x10FFff)
124 			MyCyclone.membase=(int)memory.ram-0x100000;
125 		break;
126 	case 0xC:
127 		if (pc<=0xc1FFff)
128 			MyCyclone.membase=(int)memory.rom.bios_m68k.p-0xc00000;
129 		break;
130 	}
131 
132  	if (MyCyclone.membase==-1) {
133  		printf("ERROROROOR %08x\n",pc);
134  		exit(1);
135  	}
136 
137 	return MyCyclone.membase+pc; // New program counter
138 }
139 
140 #define MEMHANDLER_READ(start,end,func) {if (a>=start && a<=end) return func(a);}
141 #define MEMHANDLER_WRITE(start,end,func) {if (a>=start && a<=end) {func(a,d);return;}}
142 
MyRead8(unsigned int a)143 static unsigned int  MyRead8  (unsigned int a) {
144 	unsigned int addr=a&0xFFFFF;
145 	unsigned int b=((a&0xF0000)>>16);
146 	a&=0xFFFFFF;
147 	switch((a&0xFF0000)>>20) {
148 	case 0x0:
149 		return (READ_BYTE_ROM(memory.rom.cpu_m68k.p + addr))&0xFF;
150 		break;
151 	case 0x2:
152 		if (memory.bksw_unscramble)
153 			return mem68k_fetch_bk_normal_byte(a);
154 		return (READ_BYTE_ROM(memory.rom.cpu_m68k.p + bankaddress + addr))&0xFF;
155 		break;
156 	case 0x1:
157 		return (READ_BYTE_ROM(memory.ram + (addr&0xFFFF)))&0xFF;
158 		break;
159 	case 0xC:
160 		if (b<=1) return (READ_BYTE_ROM(memory.rom.bios_m68k.p + addr))&0xFF;
161 		break;
162 	case 0xd:
163 		if (b==0) return mem68k_fetch_sram_byte(a)&0xFF;
164 		break;
165 	case 0x4:
166 		if (b==0) return mem68k_fetch_pal_byte(a)&0xFF;
167 		break;
168 	case 0x3:
169 		if (b==0xC) return mem68k_fetch_video_byte(a)&0xFF;
170 		if (b==0) return mem68k_fetch_ctl1_byte(a)&0xFF;
171 		if (b==4) return mem68k_fetch_ctl2_byte(a)&0xFF;
172 		if (b==8) return mem68k_fetch_ctl3_byte(a)&0xFF;
173 		if (b==2) return mem68k_fetch_coin_byte(a)&0xFF;
174 		break;
175 	case 0x8:
176 		if (b==0) return mem68k_fetch_memcrd_byte(a)&0xFF;
177 		break;
178 	}
179 
180 	return 0xFF;
181 }
MyRead16(unsigned int a)182 static unsigned int MyRead16 (unsigned int a) {
183 	unsigned int addr=a&0xFFFFF;
184 	unsigned int b=((a&0xF0000)>>16);
185 	//printf("read 32 %08x\n",a);
186 	a&=0xFFFFFF;
187 
188 	switch((a&0xFF0000)>>20) {
189 	case 0x0:
190 		return (READ_WORD_ROM(memory.rom.cpu_m68k.p + addr))&0xFFFF;
191 		break;
192 	case 0x2:
193 		if (memory.bksw_unscramble)
194 			return mem68k_fetch_bk_normal_word(a);
195 		return (READ_WORD_ROM(memory.rom.cpu_m68k.p + bankaddress + addr))&0xFFFF;
196 
197 		break;
198 	case 0x1:
199 		return (READ_WORD_ROM(memory.ram + (addr&0xFFFF)))&0xFFFF;
200 		break;
201 	case 0xC:
202 		if (b<=1) return (READ_WORD_ROM(memory.rom.bios_m68k.p + addr))&0xFFFF;
203 		break;
204 
205 	case 0xd:
206 		if (b==0) return mem68k_fetch_sram_word(a)&0xFFFF;
207 		break;
208 	case 0x4:
209 		if (b==0) return mem68k_fetch_pal_word(a)&0xFFFF;
210 		break;
211 	case 0x3:
212 		if (b==0xC) return mem68k_fetch_video_word(a)&0xFFFF;
213 		if (b==0) return mem68k_fetch_ctl1_word(a)&0xFFFF;
214 		if (b==4) return mem68k_fetch_ctl2_word(a)&0xFFFF;
215 		if (b==8) return mem68k_fetch_ctl3_word(a)&0xFFFF;
216 		if (b==2) return mem68k_fetch_coin_word(a)&0xFFFF;
217 		break;
218 	case 0x8:
219 		if (b==0) return mem68k_fetch_memcrd_word(a)&0xFFFF;
220 		break;
221 	}
222 
223 	return 0xF0F0;
224 }
MyRead32(unsigned int a)225 static unsigned int   MyRead32 (unsigned int a) {
226 	//int i;
227 	unsigned int addr=a&0xFFFFF;
228 	unsigned int b=((a&0xF0000)>>16);
229 	a&=0xFFFFFF;
230 
231 	switch((a&0xFF0000)>>20) {
232 	case 0x0:
233 		//return mem68k_fetch_cpu_long(a);
234 		return ((READ_WORD_ROM(memory.rom.cpu_m68k.p + addr))<<16) |
235 			(READ_WORD_ROM(memory.rom.cpu_m68k.p + (addr+2)));
236 		break;
237 	case 0x2:
238 		//return mem68k_fetch_bk_normal_long(a);
239 		if (memory.bksw_unscramble)
240 			return mem68k_fetch_bk_normal_long(a);
241 		return ((READ_WORD_ROM(memory.rom.cpu_m68k.p + bankaddress + addr))<<16) |
242 			(READ_WORD_ROM(memory.rom.cpu_m68k.p + bankaddress + (addr+2)));
243 		break;
244 	case 0x1:
245 		//return mem68k_fetch_ram_long(a);
246 		addr&=0xFFFF;
247 		return ((READ_WORD_ROM(memory.ram + addr))<<16) |
248 			(READ_WORD_ROM(memory.ram + (addr+2)));
249 		break;
250 	case 0xC:
251 		//return mem68k_fetch_bios_long(a);
252 		if (b<=1) return ((READ_WORD_ROM(memory.rom.bios_m68k.p + addr))<<16) |
253 				 (READ_WORD_ROM(memory.rom.bios_m68k.p + (addr+2)));
254 		break;
255 
256 	case 0xd:
257 		if (b==0) return mem68k_fetch_sram_long(a);
258 		break;
259 	case 0x4:
260 		if (b==0) return mem68k_fetch_pal_long(a);
261 		break;
262 	case 0x3:
263 		if (b==0xC) return mem68k_fetch_video_long(a);
264 		if (b==0) return mem68k_fetch_ctl1_long(a);
265 		if (b==4) return mem68k_fetch_ctl2_long(a);
266 		if (b==8) return mem68k_fetch_ctl3_long(a);
267 		if (b==2) return mem68k_fetch_coin_long(a);
268 		break;
269 	case 0x8:
270 		if (b==0) return mem68k_fetch_memcrd_long(a);
271 		break;
272 	}
273 
274 	return 0xFF00FF00;
275 }
MyWrite8(unsigned int a,unsigned int d)276 static void MyWrite8 (unsigned int a,unsigned int  d) {
277 	unsigned int b=((a&0xF0000)>>16);
278 	a&=0xFFFFFF;
279     d&=0xFF;
280 	switch((a&0xFF0000)>>20) {
281 	case 0x1:
282 		WRITE_BYTE_ROM(memory.ram + (a&0xffff),d);
283 		return ;
284 		break;
285 	case 0x3:
286 		if (b==0xc) {mem68k_store_video_byte(a,d);return;}
287 		if (b==8) {mem68k_store_pd4990_byte(a,d);return;}
288 		if (b==2) {mem68k_store_z80_byte(a,d);return;}
289 		if (b==0xA) {mem68k_store_setting_byte(a,d);return;}
290 		break;
291 	case 0x4:
292 		if (b==0) mem68k_store_pal_byte(a,d);
293 		return;
294 		break;
295 	case 0xD:
296 		if (b==0) mem68k_store_sram_byte(a,d);return;
297 		break;
298 	case 0x2:
299 		if (b==0xF) mem68k_store_bk_normal_byte(a,d);return;
300 		break;
301 	case 0x8:
302 		if (b==0) mem68k_store_memcrd_byte(a,d);return;
303 		break;
304 
305 	}
306 
307 	if(a==0x300001) memory.watchdog=0; // Watchdog
308 
309 	//printf("Unhandled write8  @ %08x = %02x\n",a,d);
310 }
MyWrite16(unsigned int a,unsigned int d)311 static void MyWrite16(unsigned int a,unsigned int d) {
312 	unsigned int b=((a&0xF0000)>>16);
313 	a&=0xFFFFFF;
314     d&=0xFFFF;
315     //if (d&0x8000) printf("WEIRD %x %x\n",a,d);
316 
317 	switch((a&0xFF0000)>>20) {
318 	case 0x1:
319 		WRITE_WORD_ROM(memory.ram + (a&0xffff),d);
320 		return;
321 		//mem68k_store_ram_word(a,d);return;
322 		break;
323 	case 0x3:
324 		if (b==0xc) {
325             mem68k_store_video_word(a,d);return;}
326 		if (b==8) {mem68k_store_pd4990_word(a,d);return;}
327 		if (b==2) {mem68k_store_z80_word(a,d);return;}
328 		if (b==0xA) {mem68k_store_setting_word(a,d);return;}
329 		break;
330 	case 0x4:
331 		if (b==0) mem68k_store_pal_word(a,d);return;
332 		break;
333 	case 0xD:
334 		if (b==0) mem68k_store_sram_word(a,d);return;
335 		break;
336 	case 0x2:
337 		if (b==0xF) mem68k_store_bk_normal_word(a,d);return;
338 		break;
339 	case 0x8:
340 		if (b==0) mem68k_store_memcrd_word(a,d);return;
341 		break;
342 	}
343 
344 	//printf("Unhandled write16 @ %08x = %04x\n",a,d);
345 }
MyWrite32(unsigned int a,unsigned int d)346 static void MyWrite32(unsigned int a,unsigned int   d) {
347 	unsigned int b=((a&0xF0000)>>16);
348 	a&=0xFFFFFF;
349     d&=0xFFFFFFFF;
350 		switch((a&0xFF0000)>>20) {
351 	case 0x1:
352 		WRITE_WORD_ROM(memory.ram + (a&0xffff),d>>16);
353 		WRITE_WORD_ROM(memory.ram + (a&0xffff)+2,d&0xFFFF);
354 		return;
355 		break;
356 	case 0x3:
357 		if (b==0xc) {
358 			mem68k_store_video_word(a,d>>16);
359 			mem68k_store_video_word(a+2,d & 0xffff);
360 			return;
361 		}
362 		if (b==8) {mem68k_store_pd4990_long(a,d);return;}
363 		if (b==2) {mem68k_store_z80_long(a,d);return;}
364 		if (b==0xA) {mem68k_store_setting_long(a,d);return;}
365 		break;
366 	case 0x4:
367 		if (b==0) mem68k_store_pal_long(a,d);return;
368 		break;
369 	case 0xD:
370 		if (b==0) mem68k_store_sram_long(a,d);return;
371 		break;
372 	case 0x2:
373 		if (b==0xF) mem68k_store_bk_normal_long(a,d);return;
374 		break;
375 	case 0x8:
376 		if (b==0) mem68k_store_memcrd_long(a,d);return;
377 		break;
378 	}
379 
380 	//printf("Unhandled write32 @ %08x = %08x\n",a,d);
381 }
382 
cpu_68k_post_load_state(void)383 static void cpu_68k_post_load_state(void) {
384 
385 	MyCyclone.read8=MyRead8;
386 	MyCyclone.read16=MyRead16;
387 	MyCyclone.read32=MyRead32;
388 
389 	MyCyclone.write8=MyWrite8;
390 	MyCyclone.write16=MyWrite16;
391 	MyCyclone.write32=MyWrite32;
392 
393 	MyCyclone.checkpc=MyCheckPc;
394 
395 	MyCyclone.fetch8  =MyRead8;
396         MyCyclone.fetch16 =MyRead16;
397         MyCyclone.fetch32 =MyRead32;
398 
399 	cpu_68k_bankswitch(bankaddress);
400 	MyCyclone.membase=0;
401 	MyCyclone.pc=MyCheckPc(cyclone_pc);
402 	//printf("Loaded PC=%08x\n",cyclone_pc);
403 }
404 
cpu_68k_pre_save_state(void)405 static void cpu_68k_pre_save_state(void) {
406 	cyclone_pc=MyCyclone.pc-MyCyclone.membase;
407 	//printf("Saved PC=%08x\n",cyclone_pc);
408 }
409 
cpu_68k_init_save_state(void)410 static void cpu_68k_init_save_state(void) {
411 
412 	create_state_register(ST_68k,"cyclone68k",1,(void *)&MyCyclone,sizeof(MyCyclone),REG_UINT8);
413 	create_state_register(ST_68k,"pc",1,(void *)&cyclone_pc,sizeof(Uint32),REG_UINT32);
414 	create_state_register(ST_68k,"bank",1,(void *)&bankaddress,sizeof(Uint32),REG_UINT32);
415 	create_state_register(ST_68k,"ram",1,(void *)memory.ram,0x10000,REG_UINT8);
416 	//create_state_register(ST_68k,"kof2003_bksw",1,(void *)memory.kof2003_bksw,0x1000,REG_UINT8);
417 	create_state_register(ST_68k,"current_vector",1,(void *)memory.rom.cpu_m68k.p,0x80,REG_UINT8);
418 	set_post_load_function(ST_68k,cpu_68k_post_load_state);
419 	set_pre_save_function(ST_68k,cpu_68k_pre_save_state);
420 
421 }
cpu_68k_mkstate(gzFile * gzf,int mode)422 void cpu_68k_mkstate(gzFile *gzf,int mode) {
423 	printf("Save state mode %s PC=%08x\n",(mode==STREAD?"READ":"WRITE"),MyCyclone.pc-MyCyclone.membase);
424 	if (mode==STWRITE) CyclonePack(&MyCyclone, save_buffer);
425 	mkstate_data(gzf, save_buffer, 128, mode);
426 	if (mode == STREAD) CycloneUnpack(&MyCyclone, save_buffer);
427 	printf("Save state Phase 2 PC=%08x\n", (mode == STREAD ? "READ" : "WRITE"), MyCyclone.pc - MyCyclone.membase);
428 }
cpu_68k_getcycle(void)429 int cpu_68k_getcycle(void) {
430 	return total_cycles-MyCyclone.cycles;
431 }
bankswitcher_init()432 void bankswitcher_init() {
433 	bankaddress=0;
434 }
cyclone_debug(unsigned short o)435 int cyclone_debug(unsigned short o) {
436 	printf("CYCLONE DEBUG %04x\n",o);
437 	return 0;
438 }
439 
cpu_68k_init(void)440 void cpu_68k_init(void) {
441 	int overclk=CF_VAL(cf_get_item_by_name("68kclock"));
442 	//printf("INIT \n");
443 	CycloneInit();
444 	memset(&MyCyclone, 0,sizeof(MyCyclone));
445 	/*
446 	swap_memory(memory.rom.cpu_m68k.p, memory.rom.cpu_m68k.size);
447 	swap_memory(memory.rom.bios_m68k.p, memory.rom.bios_m68k.size);
448 	swap_memory(memory.game_vector, 0x80);
449 	*/
450 	MyCyclone.read8=MyRead8;
451 	MyCyclone.read16=MyRead16;
452 	MyCyclone.read32=MyRead32;
453 
454 	MyCyclone.write8=MyWrite8;
455 	MyCyclone.write16=MyWrite16;
456 	MyCyclone.write32=MyWrite32;
457 
458 	MyCyclone.checkpc=MyCheckPc;
459 
460 	MyCyclone.fetch8  =MyRead8;
461         MyCyclone.fetch16 =MyRead16;
462         MyCyclone.fetch32 =MyRead32;
463 
464 	//MyCyclone.InvalidOpCallback=cyclone_debug;
465 	//MyCyclone.print_reg=print_one_reg;
466 	bankswitcher_init();
467 
468 
469 
470 	if (memory.rom.cpu_m68k.size > 0x100000) {
471 		bankaddress = 0x100000;
472 	}
473 	//cpu_68k_init_save_state();
474 
475 
476 	time_slice=(overclk==0?
477 		    200000:200000+(overclk*200000/100.0))/262.0;
478 }
479 
cpu_68k_reset(void)480 void cpu_68k_reset(void) {
481 
482 	//printf("Reset \n");
483 	MyCyclone.srh=0x27; // Set supervisor mode
484 	//CycloneSetSr(&MyCyclone,0x27);
485 	//MyCyclone.srh=0x20;
486 	//MyCyclone.irq=7;
487 	MyCyclone.irq=0;
488 	MyCyclone.a[7]=MyCyclone.read32(0);
489 
490 	MyCyclone.membase=0;
491 	MyCyclone.pc=MyCyclone.checkpc(MyCyclone.read32(4)); // Get Program Counter
492 
493 	//printf("PC=%08x\n SP=%08x\n",MyCyclone.pc-MyCyclone.membase,MyCyclone.a[7]);
494 }
495 
cpu_68k_run(Uint32 nb_cycle)496 int cpu_68k_run(Uint32 nb_cycle) {
497 	Uint32 i;
498 	if (conf.raster) {
499 		total_cycles=nb_cycle;MyCyclone.cycles=nb_cycle;
500 		CycloneRun(&MyCyclone);
501 		return -MyCyclone.cycles;
502 	} else {
503 #if 1
504 		current_line=0;
505 
506 		total_cycles=nb_cycle;
507 		//MyCyclone.cycles=nb_cycle;
508 		MyCyclone.cycles=0;//time_slice;
509 		for (i=0;i<261;i++) {
510 			MyCyclone.cycles+=time_slice;
511 			CycloneRun(&MyCyclone);
512 			current_line++;
513 		}
514 #else
515 		total_cycles=nb_cycle;MyCyclone.cycles=nb_cycle;
516 		CycloneRun(&MyCyclone);
517 #endif
518 		return -MyCyclone.cycles;
519 	}
520 		//return 0;
521 }
522 
cpu_68k_interrupt(int a)523 void cpu_68k_interrupt(int a) {
524 	//printf("Interrupt %d\n",a);
525 	MyCyclone.irq=a;
526 }
527 
cpu_68k_bankswitch(Uint32 address)528 void cpu_68k_bankswitch(Uint32 address) {
529 	//printf("Bankswitch %08x\n",address);
530 	bankaddress = address;
531 }
532 
cpu_68k_disassemble(int pc,int nb_instr)533 void cpu_68k_disassemble(int pc, int nb_instr) {
534 	/* TODO */
535 }
536 
cpu_68k_dumpreg(void)537 void cpu_68k_dumpreg(void) {
538 	int i;
539 	for(i=0;i<8;i++) printf("  d%d=%08x a%d=%08x\n",i,MyCyclone.d[i],i,MyCyclone.a[i]);
540 	/*printf("stack:\n");
541 	for (i=0;i<10;i++) {
542 		printf("%02d - %08x\n",i,mem68k_fetch_ram_long(MyCyclone.a[7]+i*4));
543 		}*/
544 
545 }
546 
cpu_68k_run_step(void)547 int cpu_68k_run_step(void) {
548 	MyCyclone.cycles=0;
549 	CycloneRun(&MyCyclone);
550 	return -MyCyclone.cycles;
551 }
552 
cpu_68k_getpc(void)553 Uint32 cpu_68k_getpc(void) {
554 	return MyCyclone.pc-MyCyclone.membase;
555 }
556 
cpu_68k_fill_state(M68K_STATE * st)557 void cpu_68k_fill_state(M68K_STATE *st) {
558 }
559 
560 
cpu_68k_set_state(M68K_STATE * st)561 void cpu_68k_set_state(M68K_STATE *st) {
562 }
563 
cpu_68k_debuger(void (* execstep)(void),void (* dump)(void))564 int cpu_68k_debuger(void (*execstep)(void),void (*dump)(void)) {
565 	/* TODO */
566 	return 0;
567 }
568 
569 
570 
571 #endif
572