FDS fixed for asm core
[fceu.git] / ppu.c
CommitLineData
98733207 1/**
2 * For whatever reason, breaking this out of fce.c made sprites not corrupt
3 */
4
5
6#include <string.h>
7#include <stdio.h>
8#include <stdlib.h>
9
10#include "types.h"
11#include "x6502.h"
12#include "fce.h"
13#include "sound.h"
14#include "svga.h"
15#include "netplay.h"
16#include "general.h"
17#include "endian.h"
18#include "version.h"
19#include "memory.h"
20
21#include "cart.h"
22#include "nsf.h"
23#include "fds.h"
24#include "ines.h"
25#include "unif.h"
26#include "cheat.h"
27
28#define MMC5SPRVRAMADR(V) &MMC5SPRVPage[(V)>>10][(V)]
29//#define MMC5BGVRAMADR(V) &MMC5BGVPage[(V)>>10][(V)]
30#define VRAMADR(V) &VPage[(V)>>10][(V)]
31
32#define V_FLIP 0x80
33#define H_FLIP 0x40
34#define SP_BACK 0x20
35
36uint8 SPRAM[0x100];
37static uint8 SPRBUF[0x100];
38
9115e7d2 39static uint8 sprlinebuf[256+8];
98733207 40extern void BGRender(uint8 *target);
41extern int tosprite;
42
43
44static int maxsprites=8;
45
46
47void FCEUI_DisableSpriteLimitation(int a)
48{
49 maxsprites=a?64:8;
50}
51
52
53//int printed=1;
54typedef struct {
55 uint8 y,no,atr,x;
56} SPR __attribute__((aligned(1)));
57
58typedef struct {
9115e7d2 59 // uint8 ca[2],atr,x;
60 uint8 ca[2],atr,x;
98733207 61 // union { int z; }
9115e7d2 62
98733207 63
64} SPRB __attribute__((aligned(1)));
65
66
67
68static uint8 nosprites,SpriteBlurp;
69
70void FetchSpriteData(void)
71{
72 SPR *spr;
73 uint8 H;
74 int n,vofs;
75
76 spr=(SPR *)SPRAM;
77 H=8;
78
79 nosprites=SpriteBlurp=0;
80
81 vofs=(unsigned int)(PPU[0]&0x8&(((PPU[0]&0x20)^0x20)>>2))<<9;
82 H+=(PPU[0]&0x20)>>2;
83
84 if(!PPU_hook)
85 for(n=63;n>=0;n--,spr++)
86 {
87 if((unsigned int)(scanline-spr->y)>=H) continue;
88
89 if(nosprites<maxsprites)
90 {
91 if(n==63) SpriteBlurp=1;
92
93 {
94 SPRB dst;
95 uint8 *C;
96 int t;
97 unsigned int vadr;
98
99 t = (int)scanline-(spr->y);
100
101 if (Sprite16)
102 vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
103 else
104 vadr = (spr->no<<4)+vofs;
105
106 if (spr->atr&V_FLIP)
107 {
108 vadr+=7;
109 vadr-=t;
110 vadr+=(PPU[0]&0x20)>>1;
111 vadr-=t&8;
112 }
113 else
114 {
115 vadr+=t;
116 vadr+=t&8;
117 }
118
119 /* Fix this geniestage hack */
120 if(MMC5Hack && geniestage!=1) C = MMC5SPRVRAMADR(vadr);
121 else C = VRAMADR(vadr);
122
9115e7d2 123
98733207 124 dst.ca[0]=C[0];
125 dst.ca[1]=C[8];
126 dst.x=spr->x;
127 dst.atr=spr->atr;
128
129
130 *(uint32 *)&SPRBUF[nosprites<<2]=*(uint32 *)&dst;
131 }
132
133 nosprites++;
134 }
135 else
136 {
137 PPU_status|=0x20;
138 break;
139 }
140 }
141 else
142 for(n=63;n>=0;n--,spr++)
143 {
144 if((unsigned int)(scanline-spr->y)>=H) continue;
145
146 if(nosprites<maxsprites)
147 {
148 if(n==63) SpriteBlurp=1;
149
150 {
151 SPRB dst;
152 uint8 *C;
153 int t;
154 unsigned int vadr;
155
156 t = (int)scanline-(spr->y);
157
158 if (Sprite16)
159 vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
160 else
161 vadr = (spr->no<<4)+vofs;
162
163 if (spr->atr&V_FLIP)
164 {
165 vadr+=7;
166 vadr-=t;
167 vadr+=(PPU[0]&0x20)>>1;
168 vadr-=t&8;
169 }
170 else
171 {
172 vadr+=t;
173 vadr+=t&8;
174 }
175
176 if(MMC5Hack) C = MMC5SPRVRAMADR(vadr);
177 else C = VRAMADR(vadr);
178 dst.ca[0]=C[0];
ea80a45b 179 if(nosprites<8)
180 {
181 PPU_hook(0x2000);
182 PPU_hook(vadr);
183 }
98733207 184 dst.ca[1]=C[8];
98733207 185 dst.x=spr->x;
186 dst.atr=spr->atr;
187
188
189 *(uint32 *)&SPRBUF[nosprites<<2]=*(uint32 *)&dst;
190 }
191
192 nosprites++;
193 }
194 else
195 {
196 PPU_status|=0x20;
197 break;
198 }
199 }
ea80a45b 200
201 if(nosprites>8) PPU_status|=0x20; /* Handle case when >8 sprites per
202 scanline option is enabled. */
203 else if(PPU_hook)
204 {
205 for(n=0;n<(8-nosprites);n++)
206 {
207 PPU_hook(0x2000);
208 PPU_hook(vofs);
209 }
210 }
211
98733207 212}
213
214#ifdef FRAMESKIP
215extern int FSkip;
216#endif
217
218void RefreshSprite(uint8 *target)
219{
9115e7d2 220 int n, minx=256;
98733207 221 SPRB *spr;
98733207 222
223 if(!nosprites) return;
224 #ifdef FRAMESKIP
225 if(FSkip)
226 {
227 if(!SpriteBlurp)
228 {
229 nosprites=0;
230 return;
231 }
232 else
233 nosprites=1;
234 }
235 #endif
236
98733207 237 nosprites--;
238 spr = (SPRB*)SPRBUF+nosprites;
239
240 for(n=nosprites;n>=0;n--,spr--)
241 {
9115e7d2 242 register uint32 J;
98733207 243
244 J=spr->ca[0]|spr->ca[1];
98733207 245
9115e7d2 246 if (J)
247 {
248 register uint8 atr,c1,c2;
249 uint8 *C;
250 uint8 *VB;
251 int x=spr->x;
252 atr=spr->atr;
253
254 if (x < minx)
255 {
256 if (minx == 256) FCEU_dwmemset(sprlinebuf,0x80808080,256); // only clear sprite buff when we encounter first sprite
257 minx = x;
258 }
98733207 259 if(n==0 && SpriteBlurp && !(PPU_status&0x40))
9115e7d2 260 {
98733207 261 int z,ze=x+8;
262 if(ze>256) {ze=256;}
263 if(ScreenON && (scanline<FSettings.FirstSLine || scanline>FSettings.LastSLine
264 #ifdef FRAMESKIP
265 || FSkip
266 #endif
267 ))
268 BGRender(target);
269
270 if(!(atr&H_FLIP))
271 {
272 for(z=x;z<ze;z++)
273 {
274 if(J&(0x80>>(z-x)))
275 {
276 if(!(target[z]&64))
277 tosprite=z;
278 }
279 }
280 }
281 else
282 {
283 for(z=x;z<ze;z++)
284 {
285 if(J&(1<<(z-x)))
286 {
287 if(!(target[z]&64))
288 tosprite=z;
289 }
290 }
291 }
292 //FCEU_DispMessage("%d, %d:%d",scanline,x,tosprite);
293 }
294
9115e7d2 295 c1=((spr->ca[0]>>1)&0x55)|(spr->ca[1]&0xAA);
296 c2=(spr->ca[0]&0x55)|((spr->ca[1]<<1)&0xAA);
297
98733207 298 C = sprlinebuf+x;
299 VB = (PALRAM+0x10)+((atr&3)<<2);
300
98733207 301 {
9115e7d2 302 J &= 0xff;
303 if(atr&SP_BACK) J |= 0x4000;
98733207 304 if (atr&H_FLIP)
305 {
9115e7d2 306 if (J&0x02) C[1]=VB[c1&3]|(J>>8);
307 if (J&0x01) *C=VB[c2&3]|(J>>8);
98733207 308 c1>>=2;c2>>=2;
9115e7d2 309 if (J&0x08) C[3]=VB[c1&3]|(J>>8);
310 if (J&0x04) C[2]=VB[c2&3]|(J>>8);
98733207 311 c1>>=2;c2>>=2;
9115e7d2 312 if (J&0x20) C[5]=VB[c1&3]|(J>>8);
313 if (J&0x10) C[4]=VB[c2&3]|(J>>8);
98733207 314 c1>>=2;c2>>=2;
9115e7d2 315 if (J&0x80) C[7]=VB[c1]|(J>>8);
316 if (J&0x40) C[6]=VB[c2]|(J>>8);
98733207 317 } else {
9115e7d2 318 if (J&0x02) C[6]=VB[c1&3]|(J>>8);
319 if (J&0x01) C[7]=VB[c2&3]|(J>>8);
98733207 320 c1>>=2;c2>>=2;
9115e7d2 321 if (J&0x08) C[4]=VB[c1&3]|(J>>8);
322 if (J&0x04) C[5]=VB[c2&3]|(J>>8);
98733207 323 c1>>=2;c2>>=2;
9115e7d2 324 if (J&0x20) C[2]=VB[c1&3]|(J>>8);
325 if (J&0x10) C[3]=VB[c2&3]|(J>>8);
98733207 326 c1>>=2;c2>>=2;
9115e7d2 327 if (J&0x80) *C=VB[c1]|(J>>8);
328 if (J&0x40) C[1]=VB[c2]|(J>>8);
98733207 329 }
98733207 330 }
98733207 331 }
9115e7d2 332 }
98733207 333
334 nosprites=0;
335 #ifdef FRAMESKIP
336 if(FSkip) return;
337 #endif
9115e7d2 338 if (minx == 256) return; // no visible sprites
98733207 339
340 {
341 uint8 n=((PPU[1]&4)^4)<<1;
9115e7d2 342 if ((int)n < minx) n = minx & 0xfc;
98733207 343 loopskie:
344 {
345 uint32 t=*(uint32 *)(sprlinebuf+n);
346 if(t!=0x80808080)
347 {
348 #ifdef LSB_FIRST
9115e7d2 349 uint32 tb=*(uint32 *)(target+n);
350 if(!(t&0x00000080) && (!(t&0x00000040) || (tb&0x00000040))) { // have sprite pixel AND (normal sprite OR behind bg with no bg)
351 tb &= ~0x000000ff; tb |= t & 0x000000ff;
98733207 352 }
353
9115e7d2 354 if(!(t&0x00008000) && (!(t&0x00004000) || (tb&0x00004000))) {
355 tb &= ~0x0000ff00; tb |= t & 0x0000ff00;
98733207 356 }
357
9115e7d2 358 if(!(t&0x00800000) && (!(t&0x00400000) || (tb&0x00400000))) {
359 tb &= ~0x00ff0000; tb |= t & 0x00ff0000;
98733207 360 }
361
9115e7d2 362 if(!(t&0x80000000) && (!(t&0x40000000) || (tb&0x40000000))) {
363 tb &= ~0xff000000; tb |= t & 0xff000000;
98733207 364 }
9115e7d2 365 *(uint32 *)(target+n)=tb;
98733207 366 #else
367 if(!(t&0x80000000))
368 {
369 if(!(t&0x40)) // Normal sprite
370 P[n]=sprlinebuf[n];
371 else if(P[n]&64) // behind bg sprite
372 P[n]=sprlinebuf[n];
373 }
374
375 if(!(t&0x800000))
376 {
377 if(!(t&0x4000)) // Normal sprite
378 P[n+1]=(sprlinebuf+1)[n];
379 else if(P[n+1]&64) // behind bg sprite
380 P[n+1]=(sprlinebuf+1)[n];
381 }
382
383 if(!(t&0x8000))
384 {
385 if(!(t&0x400000)) // Normal sprite
386 P[n+2]=(sprlinebuf+2)[n];
387 else if(P[n+2]&64) // behind bg sprite
388 P[n+2]=(sprlinebuf+2)[n];
389 }
390
391 if(!(t&0x80))
392 {
393 if(!(t&0x40000000)) // Normal sprite
394 P[n+3]=(sprlinebuf+3)[n];
395 else if(P[n+3]&64) // behind bg sprite
396 P[n+3]=(sprlinebuf+3)[n];
397 }
398 #endif
399 }
400 }
401 n+=4;
402 if(n) goto loopskie;
403 }
404}
405
406
407
408
409
410/*
411void FetchSpriteData(void)
412{
413 uint8 ns,sb;
414 SPR *spr;
415 uint8 H;
416 int n;
417 int vofs;
418 uint8 P0=PPU[0];
419
9115e7d2 420
98733207 421 spr=(SPR *)SPRAM;
422 H=8;
423
424 ns=sb=0;
425
426 vofs=(unsigned int)(P0&0x8&(((P0&0x20)^0x20)>>2))<<9;
427 H+=(P0&0x20)>>2;
428
429 if(!PPU_hook)
430 for(n=63;n>=0;n--,spr++)
431 {
432 if((unsigned int)(scanline-spr->y)>=H) continue;
433 //printf("%d, %u\n",scanline,(unsigned int)(scanline-spr->y));
434 if(ns<maxsprites)
435 {
436 if(n==63) sb=1;
437
438 {
439 SPRB dst;
440 uint8 *C;
441 int t;
442 unsigned int vadr;
443
444 t = (int)scanline-(spr->y);
445
446 if (Sprite16)
447 vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
448 else
449 vadr = (spr->no<<4)+vofs;
450
451 if (spr->atr&V_FLIP)
452 {
453 vadr+=7;
454 vadr-=t;
455 vadr+=(P0&0x20)>>1;
456 vadr-=t&8;
457 }
458 else
459 {
460 vadr+=t;
461 vadr+=t&8;
462 }
463
9115e7d2 464 // Fix this geniestage hack
98733207 465 if(MMC5Hack && geniestage!=1) C = MMC5SPRVRAMADR(vadr);
466 else C = VRAMADR(vadr);
467
9115e7d2 468
98733207 469 dst.ca[0]=C[0];
470 dst.ca[1]=C[8];
471 dst.x=spr->x;
472 dst.atr=spr->atr;
473
474 *(uint32 *)&SPRBUF[ns<<2]=*(uint32 *)&dst;
475 }
476
477 ns++;
478 }
479 else
480 {
481 PPU_status|=0x20;
482 break;
483 }
484 }
485 else
486 for(n=63;n>=0;n--,spr++)
487 {
488 if((unsigned int)(scanline-spr->y)>=H) continue;
489
490 if(ns<maxsprites)
491 {
492 if(n==63) sb=1;
493
494 {
495 SPRB dst;
496 uint8 *C;
497 int t;
498 unsigned int vadr;
499
500 t = (int)scanline-(spr->y);
501
502 if (Sprite16)
503 vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4);
504 else
505 vadr = (spr->no<<4)+vofs;
506
507 if (spr->atr&V_FLIP)
508 {
509 vadr+=7;
510 vadr-=t;
511 vadr+=(P0&0x20)>>1;
512 vadr-=t&8;
513 }
514 else
515 {
516 vadr+=t;
517 vadr+=t&8;
518 }
519
520 if(MMC5Hack) C = MMC5SPRVRAMADR(vadr);
521 else C = VRAMADR(vadr);
522 dst.ca[0]=C[0];
523 if(ns<8)
524 {
525 PPU_hook(0x2000);
526 PPU_hook(vadr);
527 }
528 dst.ca[1]=C[8];
529 dst.x=spr->x;
530 dst.atr=spr->atr;
531
532
533 *(uint32 *)&SPRBUF[ns<<2]=*(uint32 *)&dst;
534 }
535
536 ns++;
537 }
538 else
539 {
540 PPU_status|=0x20;
541 break;
542 }
543 }
544 //if(ns>=7)
545 //printf("%d %d\n",scanline,ns);
546 if(ns>8) PPU_status|=0x20; // Handle case when >8 sprites per
9115e7d2 547// scanline option is enabled.
98733207 548 else if(PPU_hook)
549 {
550 for(n=0;n<(8-ns);n++)
551 {
552 PPU_hook(0x2000);
553 PPU_hook(vofs);
554 }
555 }
556 numsprites=ns;
557 SpriteBlurp=sb;
558}
559
560
561
562void RefreshSprite(uint8 *target)
563{
9115e7d2 564
98733207 565 int n,sprindex;
566 SPRB *spr;
567 uint8 *P=target;
568
569 //if (printed) { printf("SPRB: %d SPR: %d\n", sizeof(SPRB), sizeof(SPR)); printed=0; }
570 if(!numsprites) return;
571
572 FCEU_dwmemset(sprlinebuf,0x80808080,256);
573
574 numsprites--;
575 sprindex=numsprites;
576 spr = (SPRB*)SPRBUF;
577
578 // for(n=nosprites;n>=0;n--,spr--)
579 for(n=numsprites;n>=0;n--,sprindex--)
580 {
581 uint8 J,atr,c1,c2;
582 int x=spr[sprindex].x;
583 uint8 *C;
584 uint8 *VB;
9115e7d2 585
98733207 586 P+=x;
587
588 c1=((spr[sprindex].ca[0]>>1)&0x55)|(spr[sprindex].ca[1]&0xAA);
589 c2=(spr[sprindex].ca[0]&0x55)|((spr[sprindex].ca[1]<<1)&0xAA);
590
591 J=spr[sprindex].ca[0]|spr[sprindex].ca[1];
592 atr=spr[sprindex].atr;
593
594 if(J)
9115e7d2 595 {
98733207 596 if(n==0 && SpriteBlurp && !(PPU_status&0x40))
9115e7d2 597 {
98733207 598 int z,ze=x+8;
599 if(ze>256) {ze=256;}
600 if(ScreenON && (scanline<FSettings.FirstSLine || scanline>FSettings.LastSLine
601 #ifdef FRAMESKIP
602 || FSkip
603 #endif
604 ))
605 // nothing wrong with this
606 BGRender(target);
607
608 if(!(atr&H_FLIP))
609 {
610 for(z=x;z<ze;z++)
611 {
612 if(J&(0x80>>(z-x)))
613 {
614 if(!(target[z]&64))
615 tosprite=z;
616 }
617 }
618 }
619 else
620 {
621 for(z=x;z<ze;z++)
622 {
623 if(J&(1<<(z-x)))
624 {
625 if(!(target[z]&64))
626 tosprite=z;
627 }
628 }
629 }
630 //FCEU_DispMessage("%d, %d:%d",scanline,x,tosprite);
631 }
632
633 //C = sprlinebuf+(uint8)x;
634 C = &(sprlinebuf[(uint8)x]);
635 VB = (PALRAM+0x10)+((atr&3)<<2);
636
9115e7d2 637 if(atr&SP_BACK)
98733207 638 {
639 if (atr&H_FLIP)
640 {
641 if (J&0x02) C[1]=VB[c1&3]|0x40;
642 if (J&0x01) *C=VB[c2&3]|0x40;
643 c1>>=2;c2>>=2;
644 if (J&0x08) C[3]=VB[c1&3]|0x40;
645 if (J&0x04) C[2]=VB[c2&3]|0x40;
646 c1>>=2;c2>>=2;
647 if (J&0x20) C[5]=VB[c1&3]|0x40;
648 if (J&0x10) C[4]=VB[c2&3]|0x40;
649 c1>>=2;c2>>=2;
650 if (J&0x80) C[7]=VB[c1]|0x40;
651 if (J&0x40) C[6]=VB[c2]|0x40;
652 } else {
653 if (J&0x02) C[6]=VB[c1&3]|0x40;
654 if (J&0x01) C[7]=VB[c2&3]|0x40;
655 c1>>=2;c2>>=2;
656 if (J&0x08) C[4]=VB[c1&3]|0x40;
657 if (J&0x04) C[5]=VB[c2&3]|0x40;
658 c1>>=2;c2>>=2;
659 if (J&0x20) C[2]=VB[c1&3]|0x40;
660 if (J&0x10) C[3]=VB[c2&3]|0x40;
661 c1>>=2;c2>>=2;
662 if (J&0x80) *C=VB[c1]|0x40;
663 if (J&0x40) C[1]=VB[c2]|0x40;
664 }
665 } else {
666 if (atr&H_FLIP)
667 {
668 if (J&0x02) C[1]=VB[(c1&3)];
669 if (J&0x01) *C=VB[(c2&3)];
670 c1>>=2;c2>>=2;
671 if (J&0x08) C[3]=VB[(c1&3)];
672 if (J&0x04) C[2]=VB[(c2&3)];
673 c1>>=2;c2>>=2;
674 if (J&0x20) C[5]=VB[(c1&3)];
675 if (J&0x10) C[4]=VB[(c2&3)];
676 c1>>=2;c2>>=2;
677 if (J&0x80) C[7]=VB[c1];
678 if (J&0x40) C[6]=VB[c2];
9115e7d2 679 }else{
98733207 680 if (J&0x02) C[6]=VB[(c1&3)];
681 if (J&0x01) C[7]=VB[(c2&3)];
682 c1>>=2;c2>>=2;
683 if (J&0x08) C[4]=VB[(c1&3)];
684 if (J&0x04) C[5]=VB[(c2&3)];
685 c1>>=2;c2>>=2;
686 if (J&0x20) C[2]=VB[(c1&3)];
687 if (J&0x10) C[3]=VB[(c2&3)];
688 c1>>=2;c2>>=2;
689 if (J&0x80) *C=VB[c1];
690 if (J&0x40) C[1]=VB[c2];
691 }
692 }
693 }
694 P-=x;
695 }
696
697 numsprites=0;
698 #ifdef FRAMESKIP
699 if(FSkip) return;
700 #endif
701
702 {
703 uint8 n=((PPU[1]&4)^4)<<1;
704 loopskie:
705 {
706 uint32 t=*((uint32 *)(&(sprlinebuf[n])));
707 if(t!=0x80808080)
708 {
709 #ifdef LSB_FIRST
710 if(!(t&0x80))
711 {
712 if(!(t&0x40)) // Normal sprite
713 P[n]=sprlinebuf[n];
714 else if(P[n]&64) // behind bg sprite
715 P[n]=sprlinebuf[n];
716 }
717
718 if(!(t&0x8000))
719 {
720 if(!(t&0x4000)) // Normal sprite
721 P[n+1]=(sprlinebuf+1)[n];
722 else if(P[n+1]&64) // behind bg sprite
723 P[n+1]=(sprlinebuf+1)[n];
724 }
725
726 if(!(t&0x800000))
727 {
728 if(!(t&0x400000)) // Normal sprite
729 P[n+2]=(sprlinebuf+2)[n];
730 else if(P[n+2]&64) // behind bg sprite
731 P[n+2]=(sprlinebuf+2)[n];
732 }
733
734 if(!(t&0x80000000))
735 {
736 if(!(t&0x40000000)) // Normal sprite
737 P[n+3]=(sprlinebuf+3)[n];
738 else if(P[n+3]&64) // behind bg sprite
739 P[n+3]=(sprlinebuf+3)[n];
740 }
741 #else
742 if(!(t&0x80000000))
743 {
744 if(!(t&0x40)) // Normal sprite
745 P[n]=sprlinebuf[n];
746 else if(P[n]&64) // behind bg sprite
747 P[n]=sprlinebuf[n];
748 }
749
750 if(!(t&0x800000))
751 {
752 if(!(t&0x4000)) // Normal sprite
753 P[n+1]=(sprlinebuf+1)[n];
754 else if(P[n+1]&64) // behind bg sprite
755 P[n+1]=(sprlinebuf+1)[n];
756 }
757
758 if(!(t&0x8000))
759 {
760 if(!(t&0x400000)) // Normal sprite
761 P[n+2]=(sprlinebuf+2)[n];
762 else if(P[n+2]&64) // behind bg sprite
763 P[n+2]=(sprlinebuf+2)[n];
764 }
765
766 if(!(t&0x80))
767 {
768 if(!(t&0x40000000)) // Normal sprite
769 P[n+3]=(sprlinebuf+3)[n];
770 else if(P[n+3]&64) // behind bg sprite
771 P[n+3]=(sprlinebuf+3)[n];
772 }
773 #endif
774 }
775 }
776 n+=4;
777 if(n) goto loopskie;
778 }
779}
780
781
782*/