summaryrefslogtreecommitdiff
path: root/source/common/v810_mem.c
blob: 14325c2bd3ff9daf5328042a0a583fa82135f342 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
#include "v810_cpu.h"
#include "vb_types.h"
#include "vb_dsp.h"
#include "vb_set.h"
#include "vb_sound.h"
#include "v810_mem.h"

int is_sram = 0;

// Memory read functions
BYTE mem_rbyte(WORD addr) {
    addr = addr & 0x07FFFFFF; // map to 24 bit address CFFFFFFF

    switch((addr&0x7000000)) {// switch on address
    case 0x7000000:
        return ((BYTE *)(V810_ROM1.off + (addr & V810_ROM1.highaddr)))[0];
        break;
    case 0:
        if((addr >= V810_DISPLAY_RAM.lowaddr)&&(addr <=V810_DISPLAY_RAM.highaddr)) {
            return ((BYTE *)(V810_DISPLAY_RAM.off + addr))[0];
        } else if((addr >= V810_VIPCREG.lowaddr)&&(addr <=V810_VIPCREG.highaddr)) {
            return (*V810_VIPCREG.rfuncb)(addr);
            // Mirror the Chr ram table to 078000-07FFFF
        } else  if((addr >= 0x00078000)&&(addr <= 0x0007FFFF)) {
            if(addr < 0x0007A000) //CHR 0-511
                return ((BYTE *)(V810_DISPLAY_RAM.off + (addr-0x00078000 + 0x00006000)))[0];
            else if(addr < 0x0007C000) //CHR 512-1023
                return ((BYTE *)(V810_DISPLAY_RAM.off + (addr-0x0007A000 + 0x0000E000)))[0];
            else if(addr < 0x0007E000) //CHR 1024-1535
                return ((BYTE *)(V810_DISPLAY_RAM.off + (addr-0x0007C000 + 0x00016000)))[0];
            else //CHR 1536-2047
                return ((BYTE *)(V810_DISPLAY_RAM.off + (addr-0x0007E000 + 0x0001E000)))[0];
        }
        break;
    case 0x1000000:
        if((addr >= V810_SOUND_RAM.lowaddr)&&(addr <=V810_SOUND_RAM.highaddr)) {
            //~ dtprintf(0,ferr,"\nRead  BYTE  [%08x]:%02x  //SoundRam",addr,((BYTE *)(V810_SOUND_RAM.off + addr))[0]);
            return ((BYTE *)(V810_SOUND_RAM.off + addr))[0];
        }
        break;
    case 0x5000000:
        //~ dtprintf(0,ferr,"\nRead  BYTE  [%08x]:%02x  //VBRam",addr,((BYTE *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0]);
        return ((BYTE *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0];
        break;
    case 0x6000000:
        is_sram = 1;
        //~ dtprintf(0,ferr,"\nRead  BYTE  PC:%08x [%08x]:%02x  //GameRam",PC,addr,((BYTE *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0]);
        return ((BYTE *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0];
        break;
    case 0x2000000:
        if((addr >= V810_HCREG.lowaddr)&&(addr <=V810_HCREG.highaddr)) {
            return (*V810_HCREG.rfuncb)(addr);
        }
        break;
    default:
        //~ dtprintf(0,ferr,"\nRead  BYTE  [%08x]:%02x",addr,0);
        return(0);
        break;
    }// End switch on address
    return(0); //Stops a silly compiler error
}

HWORD mem_rhword(WORD addr) {
    addr = addr & 0x07FFFFFE; // map to 24 bit address, mask first bit

    //~ if(dbg_watchpt_en)
    //~ dbg_watchpt(addr, 16, 0, 0);

    switch((addr&0x7000000)) {
    case 0x7000000:
        return ((HWORD *)(V810_ROM1.off + (addr & V810_ROM1.highaddr)))[0]; // does this work?
        break;
    case 0:
        if((addr >= V810_DISPLAY_RAM.lowaddr)&&(addr <=V810_DISPLAY_RAM.highaddr)) {
            return ((HWORD *)(V810_DISPLAY_RAM.off + addr))[0];
        } else if((addr >= V810_VIPCREG.lowaddr)&&(addr <=V810_VIPCREG.highaddr)) {
            return (*V810_VIPCREG.rfunch)(addr);
            // Mirror the Chr ram table to 078000-07FFFF
        } else  if((addr >= 0x00078000)&&(addr <= 0x0007FFFF)) {
            if(addr < 0x0007A000) //CHR 0-511
                return ((HWORD *)(V810_DISPLAY_RAM.off + (addr-0x00078000 + 0x00006000)))[0];
            else if(addr < 0x0007C000) //CHR 512-1023
                return ((HWORD *)(V810_DISPLAY_RAM.off + (addr-0x0007A000 + 0x0000E000)))[0];
            else if(addr < 0x0007E000) //CHR 1024-1535
                return ((HWORD *)(V810_DISPLAY_RAM.off + (addr-0x0007C000 + 0x00016000)))[0];
            else //CHR 1536-2047
                return ((HWORD *)(V810_DISPLAY_RAM.off + (addr-0x0007E000 + 0x0001E000)))[0];
        }
        break;
    case 0x1000000:
        if((addr >= V810_SOUND_RAM.lowaddr)&&(addr <=V810_SOUND_RAM.highaddr)) {
            //~ dtprintf(0,ferr,"\nRead  HWORD [%08x]:%04x  //SoundRam",addr,((HWORD *)(V810_SOUND_RAM.off + addr))[0]);
            return ((HWORD *)(V810_SOUND_RAM.off + addr))[0];
        }
        break;
    case 0x5000000:
        //~ dtprintf(0,ferr,"\nRead  HWORD [%08x]:%04x  //VBRam",addr,((HWORD *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0]);
        return ((HWORD *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0];
        break;
    case 0x6000000:
        is_sram = 1;
        //~ dtprintf(0,ferr,"\nRead  HWORD PC:%08x [%08x]:%04x  //GameRam",PC,addr,((HWORD *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0]);
        return ((HWORD *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0];
        break;
    case 0x2000000:
        if((addr >= V810_HCREG.lowaddr)&&(addr <=V810_HCREG.highaddr)) {
            return (*V810_HCREG.rfunch)(addr);
        }
        break;
    default:
        //~ dtprintf(0,ferr,"\nRead  HWORD [%08x]:%04x",addr,0);
        return(0);
        break;
    }
    return(0); //Stops a silly compiler error
}

WORD mem_rword(WORD addr) {
    addr = addr & 0x07FFFFFC; // map to 24 bit address, mask first 2 bytes to zero

    //~ if(dbg_watchpt_en)
    //~ dbg_watchpt(addr, 32, 0, 0);

    switch((addr&0x7000000)) {
    case 0x7000000:
        return ((WORD *)(V810_ROM1.off + (addr & V810_ROM1.highaddr)))[0];
        break;
    case 0:
        if((addr >= V810_DISPLAY_RAM.lowaddr)&&(addr <=V810_DISPLAY_RAM.highaddr)) {
            return ((WORD *)(V810_DISPLAY_RAM.off + addr))[0];
        } else if((addr >= V810_VIPCREG.lowaddr)&&(addr <=V810_VIPCREG.highaddr)) {
            return (*V810_VIPCREG.rfuncw)(addr);
            // Mirror the Chr ram table to 078000-07FFFF
        } else  if((addr >= 0x00078000)&&(addr <= 0x0007FFFF)) {
            if(addr < 0x0007A000) //CHR 0-511
                return ((WORD *)(V810_DISPLAY_RAM.off + (addr-0x00078000 + 0x00006000)))[0];
            else if(addr < 0x0007C000) //CHR 512-1023
                return ((WORD *)(V810_DISPLAY_RAM.off + (addr-0x0007A000 + 0x0000E000)))[0];
            else if(addr < 0x0007E000) //CHR 1024-1535
                return ((WORD *)(V810_DISPLAY_RAM.off + (addr-0x0007C000 + 0x00016000)))[0];
            else //CHR 1536-2047
                return ((WORD *)(V810_DISPLAY_RAM.off + (addr-0x0007E000 + 0x0001E000)))[0];
        }
        break;
    case 0x1000000:
        if((addr >= V810_SOUND_RAM.lowaddr)&&(addr <=V810_SOUND_RAM.highaddr)) {
            //~ dtprintf(0,ferr,"\nRead  WORD  [%08x]:%08x  //SoundRam",addr,((WORD *)(V810_SOUND_RAM.off + addr))[0]);
            return ((WORD *)(V810_SOUND_RAM.off + addr))[0];
        }
        break;
    case 0x5000000:
        //~ dtprintf(0,ferr,"\nRead  WORD  [%08x]:%08x  //VBRam",addr,((WORD *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0]);
        return ((WORD *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0];
        break;
    case 0x6000000:
        is_sram = 1;
        //~ dtprintf(0,ferr,"\nRead  WORD  PC:%08x [%08x]:%08x  //GameRam",PC,addr,((WORD *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0]);
        return ((WORD *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0];
        break;
    case 0x2000000:
        if((addr >= V810_HCREG.lowaddr)&&(addr <=V810_HCREG.highaddr)) {
            return (*V810_HCREG.rfuncw)(addr);
        }
        break;
    default:
        //~ dtprintf(0,ferr,"\nRead  WORD  [%08x]:%08x",addr,0);
        return(0);
        break;
    }
    return(0); //Stops a silly compiler error
}

/////////////////////////////////////////////////////////////////////////////
//Memory Write Func
void mem_wbyte(WORD addr, BYTE data) {
    int i =0;
    addr = addr & 0x07FFFFFF; // map to 24 bit address

    //~ if(dbg_watchpt_en)
    //~ dbg_watchpt(addr, 8, 1, data);

    switch((addr&0x7000000)) {
    case 0:
        if((addr >= V810_DISPLAY_RAM.lowaddr)&&(addr <=V810_DISPLAY_RAM.highaddr)) {
            ((BYTE *)(V810_DISPLAY_RAM.off + addr))[0] = data;

            if(addr < BGMAP_OFFSET) {
                //Kill it if writes to Char Table
                if(((addr>=0x00006000)&&(addr<0x00008000)) |
                        ((addr>=0x0000E000)&&(addr<0x00010000)) |
                        ((addr>=0x00016000)&&(addr<0x00018000)) |
                        ((addr>=0x0001E000)&&(addr<0x00020000))) {
                    for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
                    tDSPCACHE.ObjDataCacheInvalid=1;
                } else { //Direct Mem Writes, darn thoes fragmented memorys!!!
                    //tDSPCACHE.DDSPDataWrite=1;
                }
            }else if((addr >=OBJ_OFFSET)&&(addr < (OBJ_OFFSET+(OBJ_SIZE*1024)))) { //Writes to Obj Table
                tDSPCACHE.ObjDataCacheInvalid=1;
            } else if((addr >=BGMAP_OFFSET)&&(addr < (BGMAP_OFFSET+(14*BGMAP_SIZE)))) { //Writes to BGMap Table
                tDSPCACHE.BGCacheInvalid[((addr-BGMAP_OFFSET)/BGMAP_SIZE)]=1;
            }
        } else if((addr >= V810_VIPCREG.lowaddr)&&(addr <=V810_VIPCREG.highaddr)) {
            (*V810_VIPCREG.wfuncb)(addr,data);
            // Mirror the Chr ram table to 078000-07FFFF
        } else  if((addr >= 0x00078000)&&(addr <= 0x0007FFFF)) {
            if(addr < 0x0007A000) //CHR 0-511
                ((BYTE *)(V810_DISPLAY_RAM.off + ((addr-0x00078000) + 0x00006000)))[0] = data;
            else if(addr < 0x0007C000) //CHR 512-1023
                ((BYTE *)(V810_DISPLAY_RAM.off + ((addr-0x0007A000) + 0x0000E000)))[0] = data;
            else if(addr < 0x0007E000) //CHR 1024-1535
                ((BYTE *)(V810_DISPLAY_RAM.off + ((addr-0x0007C000) + 0x00016000)))[0] = data;
            else //CHR 1536-2047
                ((BYTE *)(V810_DISPLAY_RAM.off + ((addr-0x0007E000) + 0x0001E000)))[0] = data;
            //Invalidate, writes to Char table
            for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
            tDSPCACHE.ObjDataCacheInvalid=1;
        }
        break;
    case 0x1000000:
        if((addr >= V810_SOUND_RAM.lowaddr)&&(addr <=V810_SOUND_RAM.highaddr)) {
            //~ dtprintf(0,ferr,"\nWrite BYTE  [%08x]:%02x  //SoundRam",addr,data);
            ((BYTE *)(V810_SOUND_RAM.off + addr))[0] = data;
            sound_update(addr);
        }
        break;
    case 0x5000000:
        //~ dtprintf(0,ferr,"\nWrite BYTE  [%08x]:%02x  //VBRam",addr,data);
        ((BYTE *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0] = data;
        break;
    case 0x6000000:
        is_sram = 1;
        //~ dtprintf(0,ferr,"\nWrite BYTE  PC:%08x [%08x]:%02x  //GameRam",PC,addr,data);
        ((BYTE *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0] = data;
        break;
    case 0x2000000:
        if((addr >= V810_HCREG.lowaddr)&&(addr <=V810_HCREG.highaddr)) {
            (*V810_HCREG.wfuncb)(addr,data);
        }
        break;
    default:
        //~ dtprintf(0,ferr,"\nWrite BYTE  [%08x]:%02x",addr,data);
        break;
    }
}

void mem_whword(WORD addr, HWORD data) {
    int i = 0;
    addr = addr & 0x07FFFFFE; // map to 24 bit address, mask first bit

    //~ if(dbg_watchpt_en)
    //~ dbg_watchpt(addr, 16, 1, data);

    switch((addr&0x7000000)) {
    case 0:
        if((addr >= V810_DISPLAY_RAM.lowaddr)&&(addr <=V810_DISPLAY_RAM.highaddr)) {
            ((HWORD *)(V810_DISPLAY_RAM.off + addr))[0] = data;
            if(addr < BGMAP_OFFSET) { //Kill it if writes to Char Table
                //Kill it if writes to Char Table
                if(((addr>=0x00006000)&&(addr<0x00008000)) |
                        ((addr>=0x0000E000)&&(addr<0x00010000)) |
                        ((addr>=0x00016000)&&(addr<0x00018000)) |
                        ((addr>=0x0001E000)&&(addr<0x00020000))) {
                    for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
                    tDSPCACHE.ObjDataCacheInvalid=1;
                } else { //Direct Mem Writes, darn thoes fragmented memorys!!!
                    //tDSPCACHE.DDSPDataWrite=1;
                }
            }else if((addr >=OBJ_OFFSET)&&(addr < (OBJ_OFFSET+(OBJ_SIZE*1024)))) { //Writes to Obj Table
                tDSPCACHE.ObjDataCacheInvalid=1;
            } else if((addr >=BGMAP_OFFSET)&&(addr < (BGMAP_OFFSET+(14*BGMAP_SIZE)))) { //Writes to BGMap Table
                tDSPCACHE.BGCacheInvalid[((addr-BGMAP_OFFSET)/BGMAP_SIZE)]=1;
            }
        } else if((addr >= V810_VIPCREG.lowaddr)&&(addr <=V810_VIPCREG.highaddr)) {
            (*V810_VIPCREG.wfunch)(addr,data);
            // Mirror the Chr ram table to 078000-07FFFF
        } else  if((addr >= 0x00078000)&&(addr <= 0x0007FFFF)) {
            if(addr < 0x0007A000) //CHR 0-511
                ((HWORD *)(V810_DISPLAY_RAM.off + ((addr-0x00078000) + 0x00006000)))[0] = data;
            else if(addr < 0x0007C000) //CHR 512-1023
                ((HWORD *)(V810_DISPLAY_RAM.off + ((addr-0x0007A000) + 0x0000E000)))[0] = data;
            else if(addr < 0x0007E000) //CHR 1024-1535
                ((HWORD *)(V810_DISPLAY_RAM.off + ((addr-0x0007C000) + 0x00016000)))[0] = data;
            else //CHR 1536-2047
                ((HWORD *)(V810_DISPLAY_RAM.off + ((addr-0x0007E000) + 0x0001E000)))[0] = data;
            //Invalidate, writes to Char table
            for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
            tDSPCACHE.ObjDataCacheInvalid=1;
        }
        break;
    case 0x1000000:
        if((addr >= V810_SOUND_RAM.lowaddr)&&(addr <=V810_SOUND_RAM.highaddr)) {
            //~ dtprintf(0,ferr,"\nWrite HWORD [%08x]:%04x  //SoundRam",addr,data);
            ((HWORD *)(V810_SOUND_RAM.off + addr))[0] = data;
            sound_update(addr);
        }
        break;
    case 0x5000000:
        //~ dtprintf(0,ferr,"\nWrite HWORD [%08x]:%04x  //VBRam",addr,data);
        ((HWORD *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0] = data;
        break;
    case 0x6000000:
        is_sram = 1;
        //~ dtprintf(0,ferr,"\nWrite HWORD PC:%08x [%08x]:%04x  //GameRam",PC,addr,data);
        ((HWORD *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0] = data;
        break;
    case 0x2000000:
        if((addr >= V810_HCREG.lowaddr)&&(addr <=V810_HCREG.highaddr)) {
            (*V810_HCREG.wfunch)(addr,data);
        }
        break;
    default:
        //~ dtprintf(0,ferr,"\nWrite HWORD [%08x]:%04x",addr,data);
        break;
    }
}

void mem_wword(WORD addr, WORD data) {
    int i = 0;
    addr = addr & 0x07FFFFFC; // map to 24 bit address, mask first 2 bytes to zero

    //~ if(dbg_watchpt_en)
    //~ dbg_watchpt(addr, 32, 1, data);

    switch((addr&0x7000000)) {
    case 0:
        if((addr >= V810_DISPLAY_RAM.lowaddr)&&(addr <=V810_DISPLAY_RAM.highaddr)) {
            ((WORD *)(V810_DISPLAY_RAM.off + addr))[0] = data;
            if(addr < BGMAP_OFFSET) { //Kill it if writes to Char Table
                //Kill it if writes to Char Table
                if(((addr>=0x00006000)&&(addr<0x00008000)) |
                        ((addr>=0x0000E000)&&(addr<0x00010000)) |
                        ((addr>=0x00016000)&&(addr<0x00018000)) |
                        ((addr>=0x0001E000)&&(addr<0x00020000))) {
                    for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
                    tDSPCACHE.ObjDataCacheInvalid=1;
                } else { //Direct Mem Writes, darn thoes fragmented memorys!!!
                    //tDSPCACHE.DDSPDataWrite=1;
                }
            }else if((addr >=OBJ_OFFSET)&&(addr < (OBJ_OFFSET+(OBJ_SIZE*1024)))) { //Writes to Obj Table
                tDSPCACHE.ObjDataCacheInvalid=1;
            } else if((addr >=BGMAP_OFFSET)&&(addr < (BGMAP_OFFSET+(14*BGMAP_SIZE)))) { //Writes to BGMap Table
                tDSPCACHE.BGCacheInvalid[((addr-BGMAP_OFFSET)/BGMAP_SIZE)]=1;
            }
        } else if((addr >= V810_VIPCREG.lowaddr)&&(addr <=V810_VIPCREG.highaddr)) {
            (*V810_VIPCREG.wfuncw)(addr,data);
            // Mirror the Chr ram table to 078000-07FFFF
        } else  if((addr >= 0x00078000)&&(addr <= 0x0007FFFF)) {
            if(addr < 0x0007A000)  //CHR 0-511
                ((WORD *)(V810_DISPLAY_RAM.off + ((addr-0x00078000) + 0x00006000)))[0] = data;
            else if(addr < 0x0007C000) //CHR 512-1023
                ((WORD *)(V810_DISPLAY_RAM.off + ((addr-0x0007A000) + 0x0000E000)))[0] = data;
            else if(addr < 0x0007E000) //CHR 1024-1535
                ((WORD *)(V810_DISPLAY_RAM.off + ((addr-0x0007C000) + 0x00016000)))[0] = data;
            else      //CHR 1536-2047
                ((WORD *)(V810_DISPLAY_RAM.off + ((addr-0x0007E000) + 0x0001E000)))[0] = data;
            //Invalidate, writes to Char table
            for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
            tDSPCACHE.ObjDataCacheInvalid=1;
        }
        break;
    case 0x1000000:
        if((addr >= V810_SOUND_RAM.lowaddr)&&(addr <=V810_SOUND_RAM.highaddr)) {
            //~ dtprintf(0,ferr,"\nWrite WORD  [%08x]:%08x  //SoundRam",addr,data);
            ((WORD *)(V810_SOUND_RAM.off + addr))[0] = data;
            sound_update(addr);
        }
        break;
    case 0x5000000:
        //~ dtprintf(0,ferr,"\nWrite WORD  [%08x]:%08x  //VBRam",addr,data);
        ((WORD *)(V810_VB_RAM.off + (addr & V810_VB_RAM.highaddr)))[0] = data;
        break;
    case 0x6000000:
        is_sram = 1;
        //~ dtprintf(0,ferr,"\nWrite WORD  PC:%08x [%08x]:%08x  //GameRam",PC,addr,data);
        ((WORD *)(V810_GAME_RAM.off + (addr & V810_GAME_RAM.highaddr)))[0] = data;
        break;
    case 0x2000000:
        if((addr >= V810_HCREG.lowaddr)&&(addr <=V810_HCREG.highaddr)) {
            (*V810_HCREG.wfuncw)(addr,data);
        }
        break;
    default:
        //~ dtprintf(0,ferr,"\nWrite WORD  [%08x]:%08x",addr,data);
        break;
    }
}

//////////////////////////////////////////////////////////////////////////////
// Hardware Controll Reg....
BYTE hcreg_rbyte(WORD addr) {
    addr = (addr & 0x0200003C);
    switch(addr) {
    case 0x02000000:    //CCR
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG CCR [%08x]",addr);
        return (tHReg.CCR|0x04); //Mask the Write Only Bit...
        break;
    case 0x02000004:    //CCSR
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG CCSR [%08x]",addr);
        return (tHReg.CCSR|0x04);
        break;
    case 0x02000008:    //CDTR
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG CDTR [%08x]",addr);
        return tHReg.CDTR;
        break;
    case 0x0200000C:    //CDRR
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG CDRR [%08x]",addr);
        return tHReg.CDRR;
        break;
    case 0x02000010:    //SLB
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG SLB [%08x]",addr);
        return tHReg.SLB;
        break;
    case 0x02000014:    //SHB
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG SHB [%08x]",addr);
        return tHReg.SHB;
        break;
    case 0x02000018:    //TLB
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG TLB [%08x]",addr);
        return tHReg.TLB;//Should return the current counter???
        break;
    case 0x0200001C:    //THB
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG THB [%08x]",addr);
        return tHReg.THB;
        break;
    case 0x02000020:    //TCR
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG TCR [%08x]",addr);
        return (tHReg.TCR|0x04);
        break;
    case 0x02000024:    //WCR
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG WCR [%08x]",addr);
        return tHReg.WCR;
        break;
    case 0x02000028:    //SCR
        //~ dtprintf(3,ferr,"\nRead  BYTE HREG SCR [%08x]",addr);
        //tHReg.SCR=(tHReg.SCR^0x02); //Remove Me!!
        return (tHReg.SCR|0x4C);
        break;
    default:
        //~ dtprintf(0,ferr,"\nRead  BYTE HREG error [%08x]",addr);
        return 0;
        break;
    }
}

void hcreg_wbyte(WORD addr, BYTE data) {
    addr = (addr & 0x0200003C);
    switch(addr) {
    case 0x02000000:    //CCR
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG CCR [%08x]:%02x ",addr,data);
        tHReg.CCR = ((data|0x69)&0xFD);
        break;
    case 0x02000004:    //CCSR
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG CCSR [%08x]:%02x ",addr,data);
        tHReg.CCSR = ((data|0x60)&0xFA);
        break;
    case 0x02000008:    //CDTR
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG CDTR [%08x]:%02x ",addr,data);
        tHReg.CDTR = data;
        break;
        //case 0x0200000C:    //CDRR  // Read Only!
        //    tHReg.CDRR = data;
        //    break;
        //case 0x02000010:    //SLB
        //    tHReg.SLB = data;
        //    break;
        //case 0x02000014:    //SHB
        //    tHReg.SHB = data;
        //    break;
    case 0x02000018:    //TLB
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG TLB [%08x]:%02x ",addr,data);
        tHReg.TLB = data;
        tHReg.tTHW = (tHReg.TLB | (tHReg.tTHW & 0xFF00)); //Reset internal count
        //tHReg.tTHW = (tHReg.TLB | (tHReg.THB << 8)); //Reset internal count
        break;
    case 0x0200001C:    //THB
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG THB [%08x]:%02x ",addr,data);
        tHReg.THB = data;
        tHReg.tTHW = ((tHReg.THB << 8) | (tHReg.tTHW & 0xFF)); //Reset internal count
        //tHReg.tTHW = (tHReg.TLB | (tHReg.THB << 8)); //Reset internal count
        break;
    case 0x02000020:    //TCR
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG TCR [%08x]:%02x ",addr,data);
        if ((tHReg.TCR & 1) && ((data & 0x05) == 0x04)) break; //Cannot disable timer and clear ZStat at the same time!

        if (data & 0x01) {
            tHReg.TLB = (tHReg.tTHW&0xFF);
            tHReg.THB = ((tHReg.tTHW>>8)&0xFF);
            tHReg.tCount = tHReg.tTHW;
            if (!(tHReg.TCR & 0x01)) tHReg.tReset = 1;
        }
        if (data & 0x10) { //20us timer
            tHReg.tTRC = 400;
        }
        else { //100us timer
            if (tVBOpt.CRC32 == 0xFA44402D) tHReg.tTRC = 20000; //Space Invaders hack! 1ms
            else tHReg.tTRC = 2000; //100us
        }

        tHReg.TCR = (((data|0xE4)&0xFD)|(tHReg.TCR&0x02));
        if ((data & 0x04) && (!(tHReg.TCR & 0x01))) { //cannot clear ZStat if timer is enabled...
            tHReg.TCR &= 0xFD; // Clear the ZStat Flag...
        }
        break;
    case 0x02000024:    //WCR
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG WCR [%08x]:%02x ",addr,data);
        tHReg.WCR = (data|0xFC); // Mask
        break;
    case 0x02000028:    //SCR
        //~ dtprintf(3,ferr,"\nWrite  BYTE HCREG SCR [%08x]:%02x ",addr,data);
        tHReg.SCR = ((data|0x48)&0xFD);//0x48
        if(data & 0x80) { //Int Clear (What)
            //Clear the Pupy, but why the RETI will clear it for us....
        }
        if(data & 0x04) { //Hardware Read
            if(!(data&0x01)) { //Hardware Read Disabled
                HWORD r = V810_RControll();   //Read the Keybd
                tHReg.SLB =(BYTE)(r&0xFF);    //And stor the results...
                tHReg.SHB =(BYTE)((r>>8)&0xFF);
            }
        } else if(data & 0x20) { //Software Read, same for now....
            HWORD r = V810_RControll();   //Read the Keybd
            tHReg.SLB =(BYTE)(r&0xFF);    //And stor the results...
            tHReg.SHB =(BYTE)((r>>8)&0xFF);
        }
        break;
    default:
        //~ dtprintf(0,ferr,"\nWrite  BYTE HREG error [%08x]:%04x ",addr,data);
        break;
    }
}

HWORD hcreg_rhword(WORD addr) {
    //~ dtprintf(0,ferr,"\nRead  HWORD HReg [%08x]:%04x !!!!!!",addr,(HWORD)hcreg_rbyte(addr));
    return (HWORD)hcreg_rbyte(addr);
}
void hcreg_whword(WORD addr, HWORD data) {
    //~ dtprintf(0,ferr,"\nWrite  HWORD HReg [%08x]:%04x !!!!!!",addr,data);
    hcreg_wbyte(addr,(BYTE)data); //All read/write is byte...
}
WORD hcreg_rword(WORD addr) {
    //~ dtprintf(0,ferr,"\nRead  WORD HReg [%08x]:%08x !!!!!!",addr,(WORD)hcreg_rbyte(addr));
    return (WORD)hcreg_rbyte(addr);
}
void hcreg_wword(WORD addr, WORD data) {
    //~ dtprintf(0,ferr,"\nWrite  WORD HReg [%08x]:%08x !!!!!!",addr,data);
    hcreg_wbyte(addr,(BYTE)data); //All read/write is byte...
}

///////////////////////////////////////////////////////////////////////////////
// Port read functions
BYTE  port_rbyte(WORD addr) { return  mem_rbyte(addr); }
HWORD port_rhword(WORD addr) { return mem_rhword(addr); }
WORD  port_rword(WORD addr) { return mem_rword(addr); }

/////////////////////////////////////////////////////////////////////////////
//Port Write Func
void port_wbyte(WORD addr, BYTE data)   { mem_wbyte(addr,data); }
void port_whword(WORD addr, HWORD data) { mem_whword(addr,data); }
void port_wword(WORD addr, WORD data)   { mem_wword(addr,data); }

BYTE vipcreg_rbyte(WORD addr) {
    //~ dtprintf(0,ferr,"\nRead  BYTE VIP [%08x]:%02x **************",addr,0);
    return 0xFF;
}

void vipcreg_wbyte(WORD addr, BYTE data) {
    //~ dtprintf(0,ferr,"\nWrite  BYTE VIP [%08x]:%02x **************",addr,data);
}

HWORD vipcreg_rhword(WORD addr) {
    addr=(addr&0x0005007E); //Bring it into line
    addr=(addr|0x0005F800); //make sure all the right bits are on
    switch(addr) {
    case 0x0005F800:    //INTPND
        //~ dtprintf(4,ferr,"\nRead   HWORD VIP INTPND [%08x]:%04x ",addr,tVIPREG.INTPND);
        return tVIPREG.INTPND;
        break;
    case 0x0005F802:    //INTENB
        //~ dtprintf(4,ferr,"\nRead  HWORD VIP INTENB [%08x]:%04x ",addr,tVIPREG.INTENB);
        return tVIPREG.INTENB;
        break;
    case 0x0005F804:    //INTCLR
        //~ dtprintf(4,ferr,"\nRead  HWORD VIP INTCLR [%08x]:%04x ",addr,tVIPREG.INTCLR);
        return tVIPREG.INTCLR;
        break;
    case 0x0005F820:    //DPSTTS
        //~ dtprintf(4,ferr,"\nRead  HWORD VIP DPSTTS [%08x]:%04x ",addr,tVIPREG.DPSTTS);
        //Handled in vb_dsp.c, not!  what is the right time?
        //if(tVIPREG.DPSTTS == 0x0040) tVIPREG.DPSTTS = 0x003C;
        //else tVIPREG.DPSTTS = 0x0040;

        return tVIPREG.DPSTTS;
        break;
    case 0x0005F822:    //DPCTRL
        //~ dtprintf(4,ferr,"\nRead  HWORD VIP DPCTRL [%08x]:%04x ",addr,tVIPREG.DPCTRL);
        return tVIPREG.DPCTRL;
        break;
    case 0x0005F824:    //BRTA
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP BRTA [%08x]:%04x ",addr,tVIPREG.BRTA);
        return tVIPREG.BRTA;
        break;
    case 0x0005F826:    //BRTB
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP BRTB [%08x]:%04x ",addr,tVIPREG.BRTB);
        return tVIPREG.BRTB;
        break;
    case 0x0005F828:    //BRTC
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP BRTC [%08x]:%04x ",addr,tVIPREG.BRTC);
        return tVIPREG.BRTC;
        break;
    case 0x0005F82A:    //REST
        //~ dtprintf(7,ferr,"\nRead  HWORD VIP REST [%08x]:%04x ",addr,tVIPREG.REST);
        return tVIPREG.REST;
        break;
    case 0x0005F82E:    //FRMCYC
        //~ dtprintf(7,ferr,"\nRead  HWORD VIP FRMCYC [%08x]:%04x ",addr,tVIPREG.FRMCYC);
        return tVIPREG.FRMCYC;
        break;
    case 0x0005F830:    //CTA, ColumTable Address
        //~ dtprintf(8,ferr,"\nRead  HWORD VIP CTA [%08x]:%04x ",addr,tVIPREG.CTA);
        return tVIPREG.CTA;
        break;
    case 0x0005F840:    //XPSTTS
        //~ dtprintf(3,ferr,"\nRead  HWORD VIP XPSTTS [%08x]:%04x ",addr,tVIPREG.XPSTTS);
        //tVIPREG.XPSTTS=(tVIPREG.XPSTTS^0x0C);
        return tVIPREG.XPSTTS;
        break;
    case 0x0005F842:    //XPCTRL
        //~ dtprintf(3,ferr,"\nRead  HWORD VIP XPCTRL [%08x]:%04x ",addr,tVIPREG.XPCTRL);
        //tVIPREG.XPSTTS = 0xfff0;
        return tVIPREG.XPCTRL;
        break;
    case 0x0005F844:    //VER
        //~ dtprintf(8,ferr,"\nRead  HWORD VIP VER [%08x]:%04x ",addr,tVIPREG.VER);
        return tVIPREG.VER;
        break;
    case 0x0005F848:    //SPT0
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP SPT0 [%08x]:%04x ",addr,tVIPREG.SPT[0]);
        return tVIPREG.SPT[0];
        break;
    case 0x0005F84A:    //SPT1
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP SPT1 [%08x]:%04x ",addr,tVIPREG.SPT[1]);
        return tVIPREG.SPT[1];
        break;
    case 0x0005F84C:    //SPT2
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP SPT2 [%08x]:%04x ",addr,tVIPREG.SPT[2]);
        return tVIPREG.SPT[2];
        break;
    case 0x0005F84E:    //SPT3
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP SPT3 [%08x]:%04x ",addr,tVIPREG.SPT[3]);
        return tVIPREG.SPT[3];
        break;
    case 0x0005F860:    //GPLT0
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP GPLT0 [%08x]:%04x ",addr,tVIPREG.GPLT[0]);
        return tVIPREG.GPLT[0];
        break;
    case 0x0005F862:    //GPLT1
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP GPLT1 [%08x]:%04x ",addr,tVIPREG.GPLT[1]);
        return tVIPREG.GPLT[1];
        break;
    case 0x0005F864:    //GPLT2
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP GPLT2 [%08x]:%04x ",addr,tVIPREG.GPLT[2]);
        return tVIPREG.GPLT[2];
        break;
    case 0x0005F866:    //GPLT3
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP GPLT3 [%08x]:%04x ",addr,tVIPREG.GPLT[3]);
        return tVIPREG.GPLT[3];
        break;
    case 0x0005F868:    //JPLT0
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP JPLT0 [%08x]:%04x ",addr,tVIPREG.JPLT[0]);
        return tVIPREG.JPLT[0];
        break;
    case 0x0005F86A:    //JPLT1
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP JPLT1 [%08x]:%04x ",addr,tVIPREG.JPLT[1]);
        return tVIPREG.JPLT[1];
        break;
    case 0x0005F86C:    //JPLT2
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP JPLT2 [%08x]:%04x ",addr,tVIPREG.JPLT[2]);
        return tVIPREG.JPLT[2];
        break;
    case 0x0005F86E:    //JPLT3
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP JPLT3 [%08x]:%04x ",addr,tVIPREG.JPLT[3]);
        return tVIPREG.JPLT[3];
        break;
    case 0x0005F870:    //BKCOL
        //~ dtprintf(1,ferr,"\nRead  HWORD VIP BKCOL [%08x]:%04x ",addr,tVIPREG.BKCOL);
        return tVIPREG.BKCOL;
        break;
    default:
        //~ dtprintf(0,ferr,"\nRead  HWORD VIP error [%08x]:%04x ",addr,0xFFFF);
        return 0xFFFF;
        break;
    }
    return(0); //Stops a silly compiler error
}

void vipcreg_whword(WORD addr, HWORD data) {
    int i;
    addr=(addr&0x0005007E); //Bring it into line
    addr=(addr|0x0005F800); //make shure all the right bits are on
    switch(addr) {
    case 0x0005F800:    //INTPND
        //~ dtprintf(4,ferr,"\nWrite  HWORD VIP INTPND [%08x]:%04x ",addr,data);
        //****            tVIPREG.INTPND = data;
        break;
    case 0x0005F802:    //INTENB
        //~ dtprintf(4,ferr,"\nWrite  HWORD VIP INTENB [%08x]:%04x ",addr,data);
        tVIPREG.INTENB = data;
        break;
    case 0x0005F804:    //INTCLR
        //~ dtprintf(4,ferr,"\nWrite  HWORD VIP INTCLR [%08x]:%04x ",addr,data);
        tVIPREG.INTPND &= ~data; //Clear the Bits
        break;
    case 0x0005F820:    //DPSTTS
        //~ dtprintf(6,ferr,"\nWrite  HWORD VIP DPSTTS [%08x]:%04x ",addr,data);
        //****            tVIPREG.DPSTTS = data;
        break;
    case 0x0005F822:    //DPCTRL
        //~ dtprintf(6,ferr,"\nWrite  HWORD VIP DPCTRL [%08x]:%04x ",addr,data);
        tVIPREG.DPCTRL = data & 0x0703;
        tVIPREG.DPSTTS = (data & 0x0702) | (tVIPREG.DPSTTS & 0x00FC);
        if(data & 0x0001) {
            tVIPREG.INTPND &= 0x6000;
            tVIPREG.INTENB &= 0x6000;
        }
        break;
    case 0x0005F824:    //BRTA
        //if (debuglog) dbg_addtrc("\nWrite  HWORD VIP BRTA [%08x]:%04x ",addr,data);
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP BRTA [%08x]:%04x ",addr,data);
        tDSPCACHE.BrtPALMod = 1;  //Invalidate Brigtness Pallet Cache
        tVIPREG.BRTA = ((data & 0xFF) | 0x0100);
        break;
    case 0x0005F826:    //BRTB
        //if (debuglog) dbg_addtrc("\nWrite  HWORD VIP BRTB [%08x]:%04x ",addr,data);
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP BRTB [%08x]:%04x ",addr,data);
        tDSPCACHE.BrtPALMod = 1;  //Invalidate Brigtness Pallet Cache
        tVIPREG.BRTB = ((data & 0xFF) | 0x0100);
        break;
    case 0x0005F828:    //BRTC
        //if (debuglog) dbg_addtrc("\nWrite  HWORD VIP BRTC [%08x]:%04x ",addr,data);
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP BRTC [%08x]:%04x ",addr,data);
        tDSPCACHE.BrtPALMod = 1;  //Invalidate Brigtness Pallet Cache
        tVIPREG.BRTC = ((data & 0xFF) | 0x0100);
        break;
    case 0x0005F82A:    //REST
        //~ dtprintf(8,ferr,"\nWrite  HWORD VIP REST [%08x]:%04x ",addr,data);
        tVIPREG.REST = data;
        break;
    case 0x0005F82E:    //FRMCYC
        //~ dtprintf(8,ferr,"\nWrite  HWORD VIP FRMCYC [%08x]:%04x ",addr,data);
        tVIPREG.FRMCYC = data;
        break;
    case 0x0005F830:    //CTA
        //~ dtprintf(8,ferr,"\nWrite  HWORD VIP CTA [%08x]:%04x ",addr,data);
        tVIPREG.CTA = data;
        break;
    case 0x0005F840:    //XPSTTS
        //~ dtprintf(6,ferr,"\nWrite  HWORD VIP XPSTTS [%08x]:%04x ",addr,data);
        break;
    case 0x0005F842:    //XPCTRL
        //~ dtprintf(6,ferr,"\nWrite  HWORD VIP XPCTRL [%08x]:%04x ",addr,data);
        tVIPREG.XPCTRL = data & 0x1F03;
        tVIPREG.XPSTTS  = (tVIPREG.XPSTTS & 0x9F1C) | (data & 0x0002);
        if(data & 0x0001) {
            tVIPREG.INTPND &= 0x001F;
            tVIPREG.INTENB &= 0x001F;
        }
        break;
    case 0x0005F844:    //VER
        //~ dtprintf(8,ferr,"\nWrite  HWORD VIP VER [%08x]:%04x ",addr,data);
        tVIPREG.VER = data;
        break;
    case 0x0005F848:    //SPT0   // Pointers to the 4 OBJ groupes in OBJ Mem
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP SPT0 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjDataCacheInvalid=1;  //Invalidate Char Cache
        tVIPREG.SPT[0] = data;
        break;
    case 0x0005F84A:    //SPT1
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP SPT1 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjDataCacheInvalid=1;  //Invalidate Char Cache
        tVIPREG.SPT[1] = data;
        break;
    case 0x0005F84C:    //SPT2
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP SPT2 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjDataCacheInvalid=1;  //Invalidate Char Cache
        tVIPREG.SPT[2] = data;
        break;
    case 0x0005F84E:    //SPT3
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP SPT3 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjDataCacheInvalid=1;  //Invalidate Char Cache
        tVIPREG.SPT[3] = data;
        break;
    case 0x0005F860:    //GPLT0  //Set the current color palet for the BGMap's
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP GPLT0 [%08x]:%04x ",addr,data);
        tDSPCACHE.BgmPALMod = 1;  //Invalidate Pallet Cache
        for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
        tVIPREG.GPLT[0] = data;
        break;
    case 0x0005F862:    //GPLT1
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP GPLT1 [%08x]:%04x ",addr,data);
        tDSPCACHE.BgmPALMod = 1;  //Invalidate Pallet Cache
        for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
        tVIPREG.GPLT[1] = data;
        break;
    case 0x0005F864:    //GPLT2
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP GPLT2 [%08x]:%04x ",addr,data);
        tDSPCACHE.BgmPALMod = 1;  //Invalidate Pallet Cache
        for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
        tVIPREG.GPLT[2] = data;
        break;
    case 0x0005F866:    //GPLT3
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP GPLT3 [%08x]:%04x ",addr,data);
        tDSPCACHE.BgmPALMod = 1;  //Invalidate Pallet Cache
        for(i=0;i<14;i++) tDSPCACHE.BGCacheInvalid[i]=1;
        tVIPREG.GPLT[3] = data;
        break;
    case 0x0005F868:    //JPLT0  //Set the current color palet for the OBJ's
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP JPLT0 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjPALMod = 1;  //Invalidate Pallet Cache
        tDSPCACHE.ObjDataCacheInvalid=1;
        tVIPREG.JPLT[0] = data;
        break;
    case 0x0005F86A:    //JPLT1
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP JPLT1 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjPALMod = 1;  //Invalidate Pallet Cache
        tDSPCACHE.ObjDataCacheInvalid=1;
        tVIPREG.JPLT[1] = data;
        break;
    case 0x0005F86C:    //JPLT2
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP JPLT2 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjPALMod = 1;  //Invalidate Pallet Cache
        tDSPCACHE.ObjDataCacheInvalid=1;
        tVIPREG.JPLT[2] = data;
        break;
    case 0x0005F86E:    //JPLT3
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP JPLT3 [%08x]:%04x ",addr,data);
        tDSPCACHE.ObjPALMod = 1;  //Invalidate Pallet Cache
        tDSPCACHE.ObjDataCacheInvalid=1;
        tVIPREG.JPLT[3] = data;
        break;
    case 0x0005F870:    //BKCOL
        //~ dtprintf(1,ferr,"\nWrite  HWORD VIP BKCOL [%08x]:%04x ",addr,data);
        tDSPCACHE.BgmPALMod = 1;  //Invalidate Pallet Cache
        tDSPCACHE.ObjPALMod = 1;
        tVIPREG.BKCOL = data;
        break;
    default:
        //~ dtprintf(0,ferr,"\nWrite  HWORD VIP error [%08x]:%04x ",addr,data);
        break;
    }
}

WORD vipcreg_rword(WORD addr) {
    //~ dtprintf(0,ferr,"\nRead  WORD VIP [%08x]:%08x **************",addr,0);
    return vipcreg_rhword(addr); //More???
}

void vipcreg_wword(WORD addr, WORD data) {
    //~ dtprintf(0,ferr,"\nWrite  WORD VIP [%08x]:%08x **************",addr,data);
    vipcreg_whword(addr,(HWORD)data);
    vipcreg_whword(addr+2,(HWORD)(data>>16));
}