more properly handling new haa semantics
[drnoksnes] / ppu.h
1 /*
2  * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3  *
4  * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
5  *                           Jerremy Koot (jkoot@snes9x.com)
6  *
7  * Super FX C emulator code 
8  * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
9  *                           Gary Henderson.
10  * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
11  *
12  * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
13  * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
14  * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
15  *
16  * DOS port code contains the works of other authors. See headers in
17  * individual files.
18  *
19  * Snes9x homepage: http://www.snes9x.com
20  *
21  * Permission to use, copy, modify and distribute Snes9x in both binary and
22  * source form, for non-commercial purposes, is hereby granted without fee,
23  * providing that this license information and copyright notice appear with
24  * all copies and any derived work.
25  *
26  * This software is provided 'as-is', without any express or implied
27  * warranty. In no event shall the authors be held liable for any damages
28  * arising from the use of this software.
29  *
30  * Snes9x is freeware for PERSONAL USE only. Commercial users should
31  * seek permission of the copyright holders first. Commercial use includes
32  * charging money for Snes9x or software derived from Snes9x.
33  *
34  * The copyright holders request that bug fixes and improvements to the code
35  * should be forwarded to them so everyone can benefit from the modifications
36  * in future versions.
37  *
38  * Super NES and Super Nintendo Entertainment System are trademarks of
39  * Nintendo Co., Limited and its subsidiary companies.
40  */
41 #ifndef _PPU_H_
42 #define _PPU_H_
43
44 #define FIRST_VISIBLE_LINE 1
45
46 extern uint8 GetBank;
47 extern uint16 SignExtend [2];
48
49 #define TILE_2BIT 0
50 #define TILE_4BIT 1
51 #define TILE_8BIT 2
52
53 #define MAX_2BIT_TILES 4096
54 #define MAX_4BIT_TILES 2048
55 #define MAX_8BIT_TILES 1024
56
57 #define PPU_H_BEAM_IRQ_SOURCE   (1 << 0)
58 #define PPU_V_BEAM_IRQ_SOURCE   (1 << 1)
59 #define GSU_IRQ_SOURCE          (1 << 2)
60 #define SA1_IRQ_SOURCE          (1 << 7)
61 #define SA1_DMA_IRQ_SOURCE      (1 << 5)
62
63 struct ClipData {
64     uint32  Count [6];
65     uint32  Left [6][6];
66     uint32  Right [6][6];
67 };
68
69 struct InternalPPU {
70     bool8_32  ColorsChanged;
71     uint8  HDMA;
72     bool8_32  HDMAStarted;
73     uint8  MaxBrightness;
74     bool8_32  LatchedBlanking;
75     bool8_32  OBJChanged;
76     bool8_32  RenderThisFrame;
77     bool8_32  DirectColourMapsNeedRebuild;
78     uint32 FrameCount;
79     uint32 RenderedFramesCount;
80     uint32 DisplayedRenderedFrameCount;
81     uint32 SkippedFrames;
82     uint32 FrameSkip;
83     uint8  *TileCache [3];
84     uint8  *TileCached [3];
85     bool8_32  FirstVRAMRead;
86     bool8_32  LatchedInterlace;
87     bool8_32  DoubleWidthPixels;
88     int    RenderedScreenHeight;
89     int    RenderedScreenWidth;
90     uint32 Red [256];
91     uint32 Green [256];
92     uint32 Blue [256];
93     uint8  *XB;
94     uint16 ScreenColors [256];
95     int    PreviousLine;
96     int    CurrentLine;
97     int    Controller;
98     uint32 Joypads[5];
99     uint32 SuperScope;
100     uint32 Mouse[2];
101     int    PrevMouseX[2];
102     int    PrevMouseY[2];
103     struct ClipData Clip [2];
104 };
105
106 struct SOBJ
107 {
108     short  HPos;
109     uint16 VPos;
110     uint16 Name;
111     uint8  VFlip;
112     uint8  HFlip;
113     uint8  Priority;
114     uint8  Palette;
115     uint8  Size;
116 };
117
118 struct SPPU {
119     uint8  BGMode;
120     uint8  BG3Priority;
121     uint8  Brightness;
122
123     struct {
124         bool8_32 High;
125         uint8 Increment;
126         uint16 Address;
127         uint16 Mask1;
128         uint16 FullGraphicCount;
129         uint16 Shift;
130     } VMA;
131
132     struct {
133         uint16 SCBase;
134         uint16 VOffset;
135         uint16 HOffset;
136         uint8 BGSize;
137         uint16 NameBase;
138         uint16 SCSize;
139     } BG [4];
140
141     bool8_32  CGFLIP;
142     uint16 CGDATA [256]; 
143     uint8  FirstSprite;
144     uint8  LastSprite;
145     struct SOBJ OBJ [128];
146     uint8  OAMPriorityRotation;
147     uint16 OAMAddr;
148
149     uint8  OAMFlip;
150     uint16 OAMTileAddress;
151     uint16 IRQVBeamPos;
152     uint16 IRQHBeamPos;
153     uint16 VBeamPosLatched;
154     uint16 HBeamPosLatched;
155
156     uint8  HBeamFlip;
157     uint8  VBeamFlip;
158     uint8  HVBeamCounterLatched;
159
160     short  MatrixA;
161     short  MatrixB;
162     short  MatrixC;
163     short  MatrixD;
164     short  CentreX;
165     short  CentreY;
166     uint8  Joypad1ButtonReadPos;
167     uint8  Joypad2ButtonReadPos;
168
169     uint8  CGADD;
170     uint8  FixedColourRed;
171     uint8  FixedColourGreen;
172     uint8  FixedColourBlue;
173     uint16 SavedOAMAddr;
174     uint16 ScreenHeight;
175     uint32 WRAM;
176     uint8  BG_Forced;
177     bool8_32  ForcedBlanking;
178     bool8_32  OBJThroughMain;
179     bool8_32  OBJThroughSub;
180     uint8  OBJSizeSelect;
181     uint16 OBJNameBase;
182     bool8_32  OBJAddition;
183     uint8  OAMReadFlip;
184     uint8  OAMData [512 + 32];
185     bool8_32  VTimerEnabled;
186     bool8_32  HTimerEnabled;
187     short  HTimerPosition;
188     uint8  Mosaic;
189     bool8_32  BGMosaic [4];
190     bool8_32  Mode7HFlip;
191     bool8_32  Mode7VFlip;
192     uint8  Mode7Repeat;
193     uint8  Window1Left;
194     uint8  Window1Right;
195     uint8  Window2Left;
196     uint8  Window2Right;
197     uint8  ClipCounts [6];
198     uint8  ClipWindowOverlapLogic [6];
199     uint8  ClipWindow1Enable [6];
200     uint8  ClipWindow2Enable [6];
201     bool8_32  ClipWindow1Inside [6];
202     bool8_32  ClipWindow2Inside [6];
203     bool8_32  RecomputeClipWindows;
204     uint8  CGFLIPRead;
205     uint16 OBJNameSelect;
206     bool8_32  Need16x8Mulitply;
207     uint8  Joypad3ButtonReadPos;
208     uint8  MouseSpeed[2];
209 };
210
211 #define CLIP_OR 0
212 #define CLIP_AND 1
213 #define CLIP_XOR 2
214 #define CLIP_XNOR 3
215
216 struct SDMA {
217     bool8_32  TransferDirection;
218     bool8_32  AAddressFixed;
219     bool8_32  AAddressDecrement;
220     uint8  TransferMode;
221
222     uint8  ABank;
223     uint16 AAddress;
224     uint16 Address;
225     uint8  BAddress;
226
227     // General DMA only:
228     uint16 TransferBytes;
229
230     // H-DMA only:
231     bool8_32  HDMAIndirectAddressing;
232     uint16 IndirectAddress;
233     uint8  IndirectBank;
234     uint8  Repeat;
235     uint8  LineCount;
236     uint8  FirstLine;
237 };
238
239 START_EXTERN_C
240 void S9xUpdateScreen ();
241 void S9xResetPPU ();
242 void S9xFixColourBrightness ();
243 void S9xUpdateJoypads ();
244 void S9xProcessMouse(int which1);
245 void S9xSuperFXExec ();
246
247 void S9xSetPPU (uint8 Byte, uint16 Address);
248 uint8 S9xGetPPU (uint16 Address);
249 void S9xSetCPU (uint8 Byte, uint16 Address);
250 uint8 S9xGetCPU (uint16 Address);
251
252 void S9xInitC4 ();
253 void S9xSetC4 (uint8 Byte, uint16 Address);
254 uint8 S9xGetC4 (uint16 Address);
255 void S9xSetC4RAM (uint8 Byte, uint16 Address);
256 uint8 S9xGetC4RAM (uint16 Address);
257
258 extern struct SPPU PPU;
259 extern struct SDMA DMA [8];
260 extern struct InternalPPU IPPU;
261 END_EXTERN_C
262
263 #include "gfx.h"
264 #include "memmap.h"
265
266 STATIC INLINE uint8 REGISTER_4212()
267 {
268     GetBank = 0;
269     if (CPU.V_Counter >= PPU.ScreenHeight + FIRST_VISIBLE_LINE &&
270         CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE + 3)
271         GetBank = 1;
272
273     GetBank |= CPU.Cycles >= Settings.HBlankStart ? 0x40 : 0;
274     if (CPU.V_Counter >= PPU.ScreenHeight + FIRST_VISIBLE_LINE)
275         GetBank |= 0x80; /* XXX: 0x80 or 0xc0 ? */
276
277     return (GetBank);
278 }
279
280 STATIC INLINE void FLUSH_REDRAW ()
281 {
282         if (IPPU.PreviousLine != IPPU.CurrentLine)
283                 S9xUpdateScreen();
284 }
285
286 STATIC INLINE void REGISTER_2104 (uint8 byte)
287 {
288     if (PPU.OAMAddr >= 0x110)
289         return;
290         
291     int addr = (PPU.OAMAddr << 1) + (PPU.OAMFlip & 1);
292     
293     if (byte != PPU.OAMData [addr])
294     {
295         FLUSH_REDRAW ();
296         PPU.OAMData [addr] = byte;
297         IPPU.OBJChanged = TRUE;
298         if (addr & 0x200)
299         {
300             // X position high bit, and sprite size (x4)
301             struct SOBJ *pObj = &PPU.OBJ [(addr & 0x1f) * 4];
302
303             pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(byte >> 0) & 1];
304             pObj++->Size = byte & 2;
305             pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(byte >> 2) & 1];
306             pObj++->Size = byte & 8;
307             pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(byte >> 4) & 1];
308             pObj++->Size = byte & 32;
309             pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(byte >> 6) & 1];
310             pObj->Size = byte & 128;
311         }
312         else
313         {
314             if (addr & 1)
315             {
316                 if (addr & 2)
317                 {
318                     addr = PPU.OAMAddr >> 1;
319                     // Tile within group, priority, h and v flip.
320                     PPU.OBJ[addr].Name &= 0xFF;
321                     PPU.OBJ[addr].Name |= ((uint16) (byte & 1)) << 8;
322                     PPU.OBJ[addr].Palette = (byte >> 1) & 7;
323                     PPU.OBJ[addr].Priority = (byte >> 4) & 3;
324                     PPU.OBJ[addr].HFlip = (byte >> 6) & 1;
325                     PPU.OBJ[addr].VFlip = (byte >> 7) & 1;
326                 }
327                 else
328                 {
329                     // Sprite Y position
330                     PPU.OBJ[PPU.OAMAddr >> 1].VPos = byte;
331                 }
332             }
333             else
334             {
335                 if (addr & 2)
336                 {
337                     // Tile group
338                     
339                     PPU.OBJ[addr = PPU.OAMAddr >> 1].Name &= 0x100;
340                     PPU.OBJ[addr].Name |= byte;
341                 }
342                 else
343                 {
344                     // X position (low)
345                     PPU.OBJ[addr = PPU.OAMAddr >> 1].HPos &= 0xFF00;
346                     PPU.OBJ[addr].HPos |= byte;
347                 }
348             }
349         }
350     }
351     PPU.OAMFlip ^= 1;
352     if (!(PPU.OAMFlip & 1))
353         PPU.OAMAddr++;
354
355     Memory.FillRAM [0x2104] = byte;
356 }
357
358 STATIC INLINE void REGISTER_2118 (uint8 Byte)
359 {
360     uint32 address;
361     if (PPU.VMA.FullGraphicCount)
362     {
363         uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
364         address = (((PPU.VMA.Address & ~PPU.VMA.Mask1) +
365                          (rem >> PPU.VMA.Shift) +
366                          ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) & 0xffff;
367         Memory.VRAM [address] = Byte;
368     }
369     else
370     {
371         Memory.VRAM[address = (PPU.VMA.Address << 1) & 0xFFFF] = Byte;
372     }
373     IPPU.TileCached [TILE_2BIT][address >> 4] = FALSE;
374     IPPU.TileCached [TILE_4BIT][address >> 5] = FALSE;
375     IPPU.TileCached [TILE_8BIT][address >> 6] = FALSE;
376     if (!PPU.VMA.High)
377     {
378 #ifdef DEBUGGER
379         if (Settings.TraceVRAM && !CPU.InDMA)
380         {
381             printf ("VRAM write byte: $%04X (%d,%d)\n", PPU.VMA.Address,
382                     Memory.FillRAM[0x2115] & 3,
383                     (Memory.FillRAM [0x2115] & 0x0c) >> 2);
384         }
385 #endif  
386         PPU.VMA.Address += PPU.VMA.Increment;
387     }
388 //    Memory.FillRAM [0x2118] = Byte;
389 }
390
391 STATIC INLINE void REGISTER_2118_tile (uint8 Byte)
392 {
393     uint32 address;
394     uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
395     address = (((PPU.VMA.Address & ~PPU.VMA.Mask1) +
396                  (rem >> PPU.VMA.Shift) +
397                  ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) & 0xffff;
398     Memory.VRAM [address] = Byte;
399     IPPU.TileCached [TILE_2BIT][address >> 4] = FALSE;
400     IPPU.TileCached [TILE_4BIT][address >> 5] = FALSE;
401     IPPU.TileCached [TILE_8BIT][address >> 6] = FALSE;
402     if (!PPU.VMA.High)
403         PPU.VMA.Address += PPU.VMA.Increment;
404 //    Memory.FillRAM [0x2118] = Byte;
405 }
406
407 STATIC INLINE void REGISTER_2118_linear (uint8 Byte)
408 {
409     uint32 address;
410     Memory.VRAM[address = (PPU.VMA.Address << 1) & 0xFFFF] = Byte;
411     IPPU.TileCached [TILE_2BIT][address >> 4] = FALSE;
412     IPPU.TileCached [TILE_4BIT][address >> 5] = FALSE;
413     IPPU.TileCached [TILE_8BIT][address >> 6] = FALSE;
414     if (!PPU.VMA.High)
415         PPU.VMA.Address += PPU.VMA.Increment;
416 //    Memory.FillRAM [0x2118] = Byte;
417 }
418
419 STATIC INLINE void REGISTER_2119 (uint8 Byte)
420 {
421     uint32 address;
422     if (PPU.VMA.FullGraphicCount)
423     {
424         uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
425         address = ((((PPU.VMA.Address & ~PPU.VMA.Mask1) +
426                     (rem >> PPU.VMA.Shift) +
427                     ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) + 1) & 0xFFFF;
428         Memory.VRAM [address] = Byte;
429     }
430     else
431     {
432         Memory.VRAM[address = ((PPU.VMA.Address << 1) + 1) & 0xFFFF] = Byte;
433     }
434     IPPU.TileCached [TILE_2BIT][address >> 4] = FALSE;
435     IPPU.TileCached [TILE_4BIT][address >> 5] = FALSE;
436     IPPU.TileCached [TILE_8BIT][address >> 6] = FALSE;
437     if (PPU.VMA.High)
438     {
439 #ifdef DEBUGGER
440         if (Settings.TraceVRAM && !CPU.InDMA)
441         {
442             printf ("VRAM write word: $%04X (%d,%d)\n", PPU.VMA.Address,
443                     Memory.FillRAM[0x2115] & 3,
444                     (Memory.FillRAM [0x2115] & 0x0c) >> 2);
445         }
446 #endif  
447         PPU.VMA.Address += PPU.VMA.Increment;
448     }
449 //    Memory.FillRAM [0x2119] = Byte;
450 }
451
452 STATIC INLINE void REGISTER_2119_tile (uint8 Byte)
453 {
454     uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
455     uint32 address = ((((PPU.VMA.Address & ~PPU.VMA.Mask1) +
456                     (rem >> PPU.VMA.Shift) +
457                     ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) + 1) & 0xFFFF;
458     Memory.VRAM [address] = Byte;
459     IPPU.TileCached [TILE_2BIT][address >> 4] = FALSE;
460     IPPU.TileCached [TILE_4BIT][address >> 5] = FALSE;
461     IPPU.TileCached [TILE_8BIT][address >> 6] = FALSE;
462     if (PPU.VMA.High)
463         PPU.VMA.Address += PPU.VMA.Increment;
464 //    Memory.FillRAM [0x2119] = Byte;
465 }
466
467 STATIC INLINE void REGISTER_2119_linear (uint8 Byte)
468 {
469     uint32 address;
470     Memory.VRAM[address = ((PPU.VMA.Address << 1) + 1) & 0xFFFF] = Byte;
471     IPPU.TileCached [TILE_2BIT][address >> 4] = FALSE;
472     IPPU.TileCached [TILE_4BIT][address >> 5] = FALSE;
473     IPPU.TileCached [TILE_8BIT][address >> 6] = FALSE;
474     if (PPU.VMA.High)
475         PPU.VMA.Address += PPU.VMA.Increment;
476 //    Memory.FillRAM [0x2119] = Byte;
477 }
478
479 STATIC INLINE void REGISTER_2122(uint8 Byte)
480 {
481     // CG-RAM (palette) write
482
483     if (PPU.CGFLIP)
484     {
485         if ((Byte & 0x7f) != (PPU.CGDATA[PPU.CGADD] >> 8))
486         {
487             if (Settings.SixteenBit&& !(Settings.os9x_hack&PPU_IGNORE_PALWRITE)){
488                 FLUSH_REDRAW ();
489                         }
490             PPU.CGDATA[PPU.CGADD] &= 0x00FF;
491             PPU.CGDATA[PPU.CGADD] |= (Byte & 0x7f) << 8;
492             IPPU.ColorsChanged = TRUE;
493 #ifndef _SNESPPC
494             if (Settings.SixteenBit)
495 #endif
496                 {
497                 IPPU.Blue [PPU.CGADD] = IPPU.XB [(Byte >> 2) & 0x1f];
498                 IPPU.Green [PPU.CGADD] = IPPU.XB [(PPU.CGDATA[PPU.CGADD] >> 5) & 0x1f];
499                 IPPU.ScreenColors [PPU.CGADD] = (uint16) BUILD_PIXEL (IPPU.Red [PPU.CGADD],
500                                                              IPPU.Green [PPU.CGADD],
501                                                              IPPU.Blue [PPU.CGADD]);
502             }
503         }
504         PPU.CGADD++;
505     }
506     else
507     {
508         if (Byte != (uint8) (PPU.CGDATA[PPU.CGADD] & 0xff))
509         {
510             if (Settings.SixteenBit&& !(Settings.os9x_hack&PPU_IGNORE_PALWRITE)){
511                 FLUSH_REDRAW ();
512                         }
513             PPU.CGDATA[PPU.CGADD] &= 0x7F00;
514             PPU.CGDATA[PPU.CGADD] |= Byte;
515             IPPU.ColorsChanged = TRUE;
516 #ifndef _SNESPPC
517             if (Settings.SixteenBit)
518 #endif
519             {
520                 IPPU.Red [PPU.CGADD] = IPPU.XB [Byte & 0x1f];
521                 IPPU.Green [PPU.CGADD] = IPPU.XB [(PPU.CGDATA[PPU.CGADD] >> 5) & 0x1f];
522                 IPPU.ScreenColors [PPU.CGADD] = (uint16) BUILD_PIXEL (IPPU.Red [PPU.CGADD],
523                                                              IPPU.Green [PPU.CGADD],
524                                                              IPPU.Blue [PPU.CGADD]);
525             }
526         }
527     }
528     PPU.CGFLIP ^= 1;
529 //    Memory.FillRAM [0x2122] = Byte;
530 }
531
532 STATIC INLINE void REGISTER_2180(uint8 Byte)
533 {
534     Memory.RAM[PPU.WRAM++] = Byte;
535     PPU.WRAM &= 0x1FFFF;
536     Memory.FillRAM [0x2180] = Byte;
537 }
538 #endif