improved debug
[fceu.git] / x6502.c
CommitLineData
c62d2810 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <string.h>
22
23#include "types.h"
24#include "x6502.h"
25#include "fce.h"
26#include "sound.h"
937bf65b 27#include "cart.h"
c62d2810 28
c0bf6f9f 29#ifdef DEBUG_ASM_6502
81bd66a1 30#include <stdio.h>
31#include <stdlib.h>
c0bf6f9f 32extern uint32 PC_prev, OP_prev;
81bd66a1 33extern uint8 dreads[4];
34extern uint32 dwrites_c[2];
35extern int dread_count_c, dwrite_count_c;
36#define DummyRdMem(...)
37#else
38#define DummyRdMem RdMem
c0bf6f9f 39#endif
40
c62d2810 41X6502 X;
42uint32 timestamp;
43void FP_FASTAPASS(1) (*MapIRQHook)(int a);
44
45#define _PC X.PC
46#define _A X.A
47#define _X X.X
48#define _Y X.Y
49#define _S X.S
50#define _P X.P
51#define _PI X.mooPI
92e249b1 52//#define _PZ X.PZ // unused?
c62d2810 53#define _DB X.DB
54#define _count X.count
55#define _tcount X.tcount
56#define _IRQlow X.IRQlow
57#define _jammed X.jammed
58
59
60static INLINE uint8 RdMem(unsigned int A)
61{
937bf65b 62 // notaz: try to avoid lookup of every address at least for ROM and RAM areas
63 // I've verified that if ARead[0xfff0] points to CartBR, it is always normal ROM read.
64#if 0
65 if ((A&0x8000)/* && ARead[0xfff0] == CartBR*/) {
66 return (_DB=Page[A>>11][A]);
67 }
68#endif
69#if 0 // enabling this causes 4fps slowdown. Why?
70 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
71 return (_DB=RAM[A&0x7FF]);
72 }
73#endif
c0bf6f9f 74 _DB=ARead[A](A);
75#ifdef DEBUG_ASM_6502
81bd66a1 76 //printf("a == %x, pc == %x\n", A, _PC);
77 if (A >= 0x2000 && A != _PC && A != _PC - 1 && A != _PC + 1) {
78 dreads[dread_count_c++] = _DB;
79 if (dread_count_c > 4) { printf("dread_count out of range\n"); exit(1); }
80 }
c0bf6f9f 81#endif
82 return _DB;
c62d2810 83}
84
85static INLINE void WrMem(unsigned int A, uint8 V)
86{
937bf65b 87 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
88 RAM[A&0x7FF] = V;
89 return;
90 }
c62d2810 91 BWrite[A](A,V);
81bd66a1 92#ifdef DEBUG_ASM_6502
93 dwrites_c[dwrite_count_c++] = (A<<8)|V;
94 if (dwrite_count_c > 2) { printf("dwrite_count_c out of range\n"); exit(1); }
95#endif
c62d2810 96}
97
98static INLINE uint8 RdRAM(unsigned int A)
99{
100 return((_DB=RAM[A]));
101}
102
103static INLINE void WrRAM(unsigned int A, uint8 V)
104{
105 RAM[A]=V;
106}
107
108static INLINE void ADDCYC(int x)
109{
937bf65b 110 //_tcount+=x;
c62d2810 111 _count-=x*48;
112 timestamp+=x;
113}
114
af32b6c2 115void FASTAPASS(1) X6502_AddCycles_c(int x)
c62d2810 116{
117 ADDCYC(x);
118}
119
120static INLINE void PUSH(uint8 V)
121{
122 WrRAM(0x100+_S,V);
123 _S--;
124}
125
126static INLINE uint8 POP(void)
127{
128 _S++;
129 return(RdRAM(0x100+_S));
130}
131
937bf65b 132#if 0
c62d2810 133static uint8 ZNTable[256] = {
134 Z_FLAG,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
135 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
136 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
137 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
138 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
139 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
140 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
141 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
142 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
143 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
144 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
145 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
146 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
147 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
148 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
149 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG
150};
937bf65b 151#endif
c62d2810 152/* Some of these operations will only make sense if you know what the flag
153 constants are. */
937bf65b 154//#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);_P|=ZNTable[zort]
155//#define X_ZNT(zort) _P|=ZNTable[zort]
156#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);if(!zort) _P|=Z_FLAG;else _P|=zort&N_FLAG
c0bf6f9f 157#define X_ZNT(zort) if(!zort) _P|=Z_FLAG;else _P|=(zort&N_FLAG)
c62d2810 158
159/* Care must be taken if you want to turn this into a macro. Use { and }. */
160#define JR(); \
161{ \
162 uint32 tmp; \
163 int8 disp; \
164 disp=RdMem(_PC++); \
165 ADDCYC(1); \
166 tmp=_PC; \
167 _PC+=disp; \
168 if((tmp^_PC)&0x100) \
169 ADDCYC(1); \
170}
171
172#define LDA _A=x;X_ZN(_A)
173#define LDX _X=x;X_ZN(_X)
174#define LDY _Y=x;X_ZN(_Y)
175
176/* All of the freaky arithmetic operations. */
177#define AND _A&=x;X_ZN(_A)
937bf65b 178//#define BIT _P&=~(Z_FLAG|V_FLAG|N_FLAG);_P|=ZNTable[x&_A]&Z_FLAG;_P|=x&(V_FLAG|N_FLAG)
179#define BIT _P&=~(Z_FLAG|V_FLAG|N_FLAG);if(!(x&_A)) _P|=Z_FLAG;_P|=x&(V_FLAG|N_FLAG)
c62d2810 180#define EOR _A^=x;X_ZN(_A)
181#define ORA _A|=x;X_ZN(_A)
182
183#define ADC { \
184 uint32 l=_A+x+(_P&1); \
185 _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \
186 _P|=((((_A^x)&0x80)^0x80) & ((_A^l)&0x80))>>1; \
187 _P|=(l>>8)&C_FLAG; \
188 _A=l; \
189 X_ZNT(_A); \
190 }
191#define SBC { \
192 uint32 l=_A-x-((_P&1)^1); \
193 _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \
194 _P|=((_A^l)&(_A^x)&0x80)>>1; \
195 _P|=((l>>8)&C_FLAG)^C_FLAG; \
196 _A=l; \
197 X_ZNT(_A); \
198 }
199
200#define CMPL(a1,a2) { \
201 uint32 t=a1-a2; \
202 X_ZN(t&0xFF); \
203 _P&=~C_FLAG; \
204 _P|=((t>>8)&C_FLAG)^C_FLAG; \
205 }
206
207/* Special undocumented operation. Very similar to CMP. */
208#define AXS { \
209 uint32 t=(_A&_X)-x; \
210 X_ZN(t&0xFF); \
211 _P&=~C_FLAG; \
212 _P|=((t>>8)&C_FLAG)^C_FLAG; \
213 _X=t; \
214 }
215
216#define CMP CMPL(_A,x)
217#define CPX CMPL(_X,x)
218#define CPY CMPL(_Y,x)
219
220/* The following operations modify the byte being worked on. */
221#define DEC x--;X_ZN(x)
222#define INC x++;X_ZN(x)
223
224#define ASL _P&=~C_FLAG;_P|=x>>7;x<<=1;X_ZN(x)
225#define LSR _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=x&1;x>>=1;X_ZNT(x)
226
227/* For undocumented instructions, maybe for other things later... */
228#define LSRA _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=_A&1;_A>>=1;X_ZNT(_A)
229
230#define ROL { \
231 uint8 l=x>>7; \
232 x<<=1; \
233 x|=_P&C_FLAG; \
234 _P&=~(Z_FLAG|N_FLAG|C_FLAG); \
235 _P|=l; \
236 X_ZNT(x); \
237 }
238#define ROR { \
239 uint8 l=x&1; \
240 x>>=1; \
241 x|=(_P&C_FLAG)<<7; \
242 _P&=~(Z_FLAG|N_FLAG|C_FLAG); \
243 _P|=l; \
244 X_ZNT(x); \
245 }
937bf65b 246
c62d2810 247/* Icky icky thing for some undocumented instructions. Can easily be
248 broken if names of local variables are changed.
249*/
250
251/* Absolute */
252#define GetAB(target) \
253{ \
254 target=RdMem(_PC++); \
255 target|=RdMem(_PC++)<<8; \
256}
257
258/* Absolute Indexed(for reads) */
259#define GetABIRD(target, i) \
260{ \
261 unsigned int tmp; \
262 GetAB(tmp); \
263 target=tmp; \
264 target+=i; \
265 if((target^tmp)&0x100) \
266 { \
267 target&=0xFFFF; \
81bd66a1 268 DummyRdMem(target^0x100); \
c62d2810 269 ADDCYC(1); \
270 } \
271}
272
273/* Absolute Indexed(for writes and rmws) */
274#define GetABIWR(target, i) \
275{ \
276 unsigned int rt; \
277 GetAB(rt); \
278 target=rt; \
279 target+=i; \
280 target&=0xFFFF; \
81bd66a1 281 DummyRdMem((target&0x00FF)|(rt&0xFF00)); \
c62d2810 282}
283
284/* Zero Page */
285#define GetZP(target) \
286{ \
287 target=RdMem(_PC++); \
288}
289
290/* Zero Page Indexed */
291#define GetZPI(target,i) \
292{ \
293 target=i+RdMem(_PC++); \
294}
295
296/* Indexed Indirect */
297#define GetIX(target) \
298{ \
299 uint8 tmp; \
300 tmp=RdMem(_PC++); \
301 tmp+=_X; \
302 target=RdRAM(tmp++); \
303 target|=RdRAM(tmp)<<8; \
304}
305
306/* Indirect Indexed(for reads) */
307#define GetIYRD(target) \
308{ \
309 unsigned int rt; \
310 uint8 tmp; \
311 tmp=RdMem(_PC++); \
312 rt=RdRAM(tmp++); \
313 rt|=RdRAM(tmp)<<8; \
314 target=rt; \
315 target+=_Y; \
316 if((target^rt)&0x100) \
317 { \
318 target&=0xFFFF; \
81bd66a1 319 DummyRdMem(target^0x100); \
c62d2810 320 ADDCYC(1); \
321 } \
322}
323
324/* Indirect Indexed(for writes and rmws) */
325#define GetIYWR(target) \
326{ \
327 unsigned int rt; \
328 uint8 tmp; \
329 tmp=RdMem(_PC++); \
330 rt=RdRAM(tmp++); \
331 rt|=RdRAM(tmp)<<8; \
332 target=rt; \
333 target+=_Y; \
81bd66a1 334 DummyRdMem((target&0x00FF)|(rt&0xFF00)); \
c62d2810 335}
336
337/* Now come the macros to wrap up all of the above stuff addressing mode functions
338 and operation macros. Note that operation macros will always operate(redundant
339 redundant) on the variable "x".
340*/
341
342#define RMW_A(op) {uint8 x=_A; op; _A=x; break; } /* Meh... */
343#define RMW_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
344#define RMW_ABI(reg,op) {unsigned int A; uint8 x; GetABIWR(A,reg); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
345#define RMW_ABX(op) RMW_ABI(_X,op)
346#define RMW_ABY(op) RMW_ABI(_Y,op)
347#define RMW_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
348#define RMW_IY(op) {unsigned int A; uint8 x; GetIYWR(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
349#define RMW_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; WrRAM(A,x); break; }
350#define RMW_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; WrRAM(A,x); break;}
351
352#define LD_IM(op) {uint8 x; x=RdMem(_PC++); op; break;}
353#define LD_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; break;}
354#define LD_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; break;}
355#define LD_ZPY(op) {uint8 A; uint8 x; GetZPI(A,_Y); x=RdRAM(A); op; break;}
356#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; }
357#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;}
358#define LD_ABX(op) LD_ABI(_X,op)
359#define LD_ABY(op) LD_ABI(_Y,op)
360#define LD_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); op; break;}
361#define LD_IY(op) {unsigned int A; uint8 x; GetIYRD(A); x=RdMem(A); op; break;}
362
363#define ST_ZP(r) {uint8 A; GetZP(A); WrRAM(A,r); break;}
364#define ST_ZPX(r) {uint8 A; GetZPI(A,_X); WrRAM(A,r); break;}
365#define ST_ZPY(r) {uint8 A; GetZPI(A,_Y); WrRAM(A,r); break;}
366#define ST_AB(r) {unsigned int A; GetAB(A); WrMem(A,r); break;}
367#define ST_ABI(reg,r) {unsigned int A; GetABIWR(A,reg); WrMem(A,r); break; }
368#define ST_ABX(r) ST_ABI(_X,r)
369#define ST_ABY(r) ST_ABI(_Y,r)
370#define ST_IX(r) {unsigned int A; GetIX(A); WrMem(A,r); break; }
371#define ST_IY(r) {unsigned int A; GetIYWR(A); WrMem(A,r); break; }
372
373static uint8 CycTable[256] =
937bf65b 374{
c62d2810 375/*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
376/*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
377/*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
378/*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
379/*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
380/*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
381/*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
382/*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
383/*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
384/*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
385/*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
386/*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,
387/*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
388/*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
389/*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,
390/*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
391};
392
af32b6c2 393void FASTAPASS(1) X6502_IRQBegin_c(int w)
c62d2810 394{
395 _IRQlow|=w;
396}
397
af32b6c2 398void FASTAPASS(1) X6502_IRQEnd_c(int w)
c62d2810 399{
400 _IRQlow&=~w;
401}
402
af32b6c2 403void TriggerIRQ_c(void) /* This function should probably be phased out. */
c62d2810 404{
405 _IRQlow|=FCEU_IQTEMP;
406}
407
af32b6c2 408void TriggerNMINSF_c(void)
c62d2810 409{
410 ADDCYC(7);
411 PUSH(_PC>>8);
412 PUSH(_PC);
413 PUSH((_P&~B_FLAG)|(U_FLAG));
414 _PC=0x3800;
415}
416
af32b6c2 417void TriggerNMI_c(void)
c62d2810 418{
419 _IRQlow|=FCEU_IQNMI;
420}
421
422static void TriggerNMIReal(void)
423{
424 if(!_jammed)
425 {
426 ADDCYC(7);
427 PUSH(_PC>>8);
428 PUSH(_PC);
c0bf6f9f 429 _P&=~B_FLAG;
430 PUSH(_P|U_FLAG);
c62d2810 431 _PC=RdMem(0xFFFA);
432 _PC|=RdMem(0xFFFB)<<8;
c0bf6f9f 433#ifdef DEBUG_ASM_6502
434 PC_prev = _PC;
435 OP_prev = 0x100;
436#endif
c62d2810 437 }
438}
439
440void TriggerIRQReal(void)
441{
442 if(!(_PI&I_FLAG) && !_jammed)
443 {
444 ADDCYC(7);
445 PUSH(_PC>>8);
446 PUSH(_PC);
c0bf6f9f 447 _P&=~B_FLAG;
448 PUSH(_P|U_FLAG);
c62d2810 449 _P|=I_FLAG;
450 _PC=RdMem(0xFFFE);
451 _PC|=RdMem(0xFFFF)<<8;
c0bf6f9f 452#ifdef DEBUG_ASM_6502
453 PC_prev = _PC;
454 OP_prev = 0x101;
455#endif
c62d2810 456 }
457}
458
af32b6c2 459void X6502_Reset_c(void)
c62d2810 460{
461 _PC=RdMem(0xFFFC);
462 _PC|=RdMem(0xFFFD)<<8;
463 if(FCEUGameInfo.type==GIT_NSF) _PC=0x3830;
464 _jammed=0;
465 _PI=_P=I_FLAG;
466}
467
af32b6c2 468void X6502_Power_c(void)
c62d2810 469{
937bf65b 470 memset((void *)&X,0,sizeof(X));
c62d2810 471 timestamp=0;
af32b6c2 472 X6502_Reset_c();
c62d2810 473}
474
937bf65b 475
476//int asdc = 0;
af32b6c2 477void X6502_Run_c(void/*int32 cycles*/)
c62d2810 478{
937bf65b 479/*
c62d2810 480 if(PAL)
481 cycles*=15; // 15*4=60
482 else
483 cycles*=16; // 16*4=64
484
485 _count+=cycles;
937bf65b 486*/
487// if (_count <= 0) asdc++;
c62d2810 488
489 while(_count>0)
490 {
491 int32 temp;
492 uint8 b1;
493
494 if(_IRQlow)
495 {
496 if(_IRQlow&FCEU_IQNMI)
497 TriggerNMIReal();
498 else
499 TriggerIRQReal();
500
501 _IRQlow&=~(FCEU_IQTEMP|FCEU_IQNMI);
502 if(_count<=0) {_PI=_P;return;} /* Should increase accuracy without a */
503 /* major speed hit. */
504 }
505 _PI=_P;
506 b1=RdMem(_PC);
937bf65b 507 temp=CycTable[b1];
508 ADDCYC(temp);
509 //temp=_tcount;
510 //_tcount=0;
92e249b1 511
c62d2810 512 if(MapIRQHook) MapIRQHook(temp);
513
514 temp*=48;
515
516 fhcnt-=temp;
517 if(fhcnt<=0)
518 {
519 FrameSoundUpdate();
520 fhcnt+=fhinc;
521 }
522
c62d2810 523 if(PCMIRQCount>0)
524 {
525 PCMIRQCount-=temp;
526 if(PCMIRQCount<=0)
527 {
528 vdis=1;
529 if((PSG[0x10]&0x80) && !(PSG[0x10]&0x40))
530 {
531 extern uint8 SIRQStat;
532 SIRQStat|=0x80;
af32b6c2 533 X6502_IRQBegin_c(FCEU_IQDPCM);
c62d2810 534 }
535 }
536 }
92e249b1 537
c0bf6f9f 538#ifdef DEBUG_ASM_6502
539 PC_prev = _PC;
540 OP_prev = b1;
c0bf6f9f 541#endif
c62d2810 542 //printf("$%04x:$%02x\n",_PC,b1);
543 //_PC++;
544 //printf("$%02x\n",b1);
545 _PC++;
546 switch(b1)
547 {
548 #include "ops.h"
937bf65b 549 }
af32b6c2 550
551#ifdef DEBUG_ASM_6502
552 _PI=_P;
553#endif
c62d2810 554 }
555}
937bf65b 556
557