ppu.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. /*
  2. * PPU emulation - The peTI-NESulator Project
  3. * ppu.c
  4. *
  5. * Define and emulate the PPU (Picture Processing Unit) of the real NES
  6. *
  7. * Created by Manoel TRAPIER.
  8. * Copyright (c) 2003-2018 986-Studio. All rights reserved.
  9. *
  10. * $LastChangedDate$
  11. * $Author$
  12. * $HeadURL$
  13. * $Revision$
  14. *
  15. */
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <stdint.h>
  19. #include <os_dependent.h>
  20. #define __TINES_PPU_INTERNAL__
  21. #include <ppu/ppu.h>
  22. #include <ppu/ppu.memory.h>
  23. #include <ppu/ppu.debug.h>
  24. #include <memory/manager.h>
  25. #include <os_dependent.h>
  26. #define __TINES_PLUGINS__
  27. #include <plugins/manager.h>
  28. extern int VBLANK_TIME;
  29. extern volatile int frame;
  30. extern volatile unsigned long IPS, FPS;
  31. extern unsigned long ColorPalette[9 * 63];
  32. extern short IRQScanHit;
  33. extern short SZHit;
  34. typedef struct spriteData
  35. {
  36. uint8_t palette;
  37. uint8_t flip_h;
  38. uint8_t flip_v;
  39. uint8_t priority;
  40. uint8_t tile;
  41. uint8_t bank;
  42. uint8_t y;
  43. uint8_t x;
  44. uint8_t rel_y;
  45. uint8_t inUse;
  46. } spriteData;
  47. /* PPU registers */
  48. /* NT: Name Table */
  49. uint8_t PPU_Reg_NT;
  50. /* AT: Attribute/Color Table */
  51. uint8_t PPU_Reg_AT;
  52. /* FV: Fine Vertical Scroll latch/counter */
  53. uint8_t PPU_Reg_FV;
  54. /* HV: Fine Horizontal Scroll latch/counter */
  55. uint8_t PPU_Reg_FH;
  56. /* VT: Vertical Tile indev latch/counter */
  57. uint8_t PPU_Reg_VT;
  58. /* HT: Horizontal Tile indev latch/counter */
  59. uint8_t PPU_Reg_HT;
  60. /* V: Vertical Name Table Selection latch/counter */
  61. uint8_t PPU_Reg_V;
  62. /* H: Horizontal Name Table Selection latch/counter */
  63. uint8_t PPU_Reg_H;
  64. /* S: Playfield pattern table selection latch */
  65. unsigned short PPU_Reg_S;
  66. /* PAR: Picture Address Register */
  67. uint8_t PPU_Reg_PAR;
  68. /* AR: Tile Attribute (palette select) value latch */
  69. uint8_t PPU_Reg_AR;
  70. unsigned short PPU_Reg_Counter;
  71. /* PPU Memory Areas */
  72. uint8_t *ppu_mem_nameTables;
  73. uint8_t *ppu_mem_patternTables;
  74. uint8_t *ppu_mem_paletteValues;
  75. uint8_t ppu_mem_spritesTable[0x100];
  76. uint8_t ppu_mem_sptrTablePtr;
  77. /* Some other PPU "registers" */
  78. uint8_t ppu_VramAccessFlipFlop;
  79. uint8_t ppu_inVBlankTime;
  80. uint8_t ppu_spriteZeroHit;
  81. uint8_t ppu_scanlineSpriteOverflow;
  82. uint8_t ppu_bgColor;
  83. /* CR #1 variables */
  84. uint16_t ppu_spritePatternTable;
  85. uint8_t ppu_spriteSize;
  86. uint8_t ppu_addrIncrement;
  87. uint8_t ppu_execNMIonVBlank;
  88. /* CR #2 variables */
  89. uint8_t ppu_spriteVisibility;
  90. uint8_t ppu_backgroundVisibility;
  91. uint8_t ppu_spriteClipping;
  92. uint8_t ppu_backgroundClipping;
  93. uint8_t ppu_displayType;
  94. uint8_t ppu_mirrorMode;
  95. uint8_t ppu_singleScreenMode;
  96. uint8_t ppu_screenMode;
  97. #define PPU_MEM_PATTERNTABLES_SIZE 0x2000
  98. #define PPU_MEM_NAMETABLE_SIZE 0x1000
  99. #define PPU_MEM_PALETTEVALUES_SIZE 0x100 /* in fact its 20 but we must allocate a least one page */
  100. #define PPU_SPRITE_FLAGS_VFLIP ( 1 << 7 )
  101. #define PPU_SPRITE_FLAGS_HFLIP ( 1 << 6 )
  102. #define PPU_SPRITE_FLAGS_BGPRIO ( 1 << 5 )
  103. #define PPU_SPRITE_FLAGS_UPPERCOLOR ( 0x03 )
  104. #define PPU_FLAG_SR_VBLANK ( 1 << 7 )
  105. #define PPU_FLAG_SR_SPRT0 ( 1 << 6 )
  106. #define PPU_FLAG_SR_8SPRT ( 1 << 5 )
  107. #define PPU_FLAG_SR_RDWRALLOW ( 1 << 4 )
  108. #define PPU_CR1_SPRTSIZE ( 1 << 5 )
  109. #define PPU_CR1_EXECNMI ( 1 << 7 )
  110. #define PPU_CR2_BGVISIBILITY ( 1 << 3 )
  111. #define PPU_CR2_SPRTVISIBILITY ( 1 << 4 )
  112. int ppu_init()
  113. {
  114. int i;
  115. /*uint8_t defaultColors[] = { 0x09,0x01,0x00,0x01,0x00,0x02,0x02,0x0D,0x08,0x10,0x08,0x24,0x00,0x00,0x04,0x2C,
  116. 0x09,0x01,0x34,0x03,0x00,0x04,0x00,0x14,0x08,0x3A,0x00,0x02,0x00,0x20,0x2C,0x08 };*/
  117. if ( ppu_initMemory() )
  118. return -1;
  119. /* Set ppu memory parameters */
  120. /* First: Allocate each memory zone */
  121. ppu_mem_patternTables = (uint8_t *) malloc(PPU_MEM_PATTERNTABLES_SIZE);
  122. if ( !ppu_mem_patternTables )
  123. return -1;
  124. ppu_mem_nameTables = (uint8_t *) malloc(PPU_MEM_NAMETABLE_SIZE);
  125. if ( !ppu_mem_nameTables )
  126. return -1;
  127. ppu_mem_paletteValues = (uint8_t *) malloc(PPU_MEM_PALETTEVALUES_SIZE);
  128. if ( !ppu_mem_paletteValues )
  129. return -1;
  130. console_printf(Console_Default, "ppu_mem_nameTables :%p\n"
  131. "ppu_mem_patternTables:%p\n"
  132. "ppu_mem_paletteValues:%p\n",
  133. ppu_mem_nameTables,
  134. ppu_mem_patternTables,
  135. ppu_mem_paletteValues);
  136. /* Second: make the ppu memory manager point on the memory zones */
  137. ppu_setPagePtr8k(0x00, ppu_mem_patternTables);
  138. ppu_setPagePtr4k(0x20, ppu_mem_nameTables);
  139. ppu_setPagePtr(0x3F, ppu_mem_paletteValues);
  140. for ( i = 0x00 ; i < 0x0F ; i++ )
  141. ppu_setPageGhost(0x30 + i, true, 0x20 + i);
  142. /* Third: set registers to defaults */
  143. /* Now test the memory ! */
  144. /* Fille PPU memory with garbage */
  145. for ( i = 0x0000 ; i < 0x2000 ; i++ )
  146. ppu_mem_patternTables[i] = rand() % 0xFF;
  147. for ( i = 0x0000 ; i < 0x1000 ; i++ )
  148. ppu_mem_nameTables[i] = rand() % 0xFF;
  149. for ( i = 0x0000 ; i < 0x001F ; i++ )
  150. ppu_mem_paletteValues[i] = rand() % 0xFF;
  151. //memcpy(ppu_mem_paletteValues, defaultColors, 32);
  152. /* Set some other variables */
  153. ppu_VramAccessFlipFlop = 0;
  154. ppu_addrIncrement = 1;
  155. ppu_spritePatternTable = 0;
  156. ppu_spriteSize = 8;
  157. ppu_execNMIonVBlank = 0;
  158. ppu_spriteVisibility = 0;
  159. ppu_backgroundVisibility = 0;
  160. ppu_spriteClipping = 0;
  161. ppu_backgroundClipping = 0;
  162. ppu_displayType = 0;
  163. ppu_inVBlankTime = 0;
  164. ppu_bgColor = 0;
  165. /* Set PPU registers on CPU side */
  166. set_page_rd_hook(0x20, ppu_readReg);
  167. set_page_wr_hook(0x20, ppu_writeReg);
  168. set_page_readable(0x20, true);
  169. set_page_writeable(0x20, true);
  170. /* Set PPU Ghost Registers */
  171. for ( i = 0x21 ; i < 0x40 ; i++ )
  172. set_page_ghost(i, true, 0x20);
  173. /* allocate the PPU Video memory */
  174. graphics_init();
  175. return 0;
  176. }
  177. void ppu_setMirroring(uint8_t direction)
  178. {
  179. if ( ppu_screenMode != PPU_SCMODE_NORMAL )
  180. return;
  181. if ( ppu_mirrorMode == direction )
  182. return; /* Same value, no need to change! */
  183. switch(direction)
  184. {
  185. default:
  186. direction = PPU_MIRROR_HORIZTAL;
  187. ppu_mirrorMode = direction;
  188. case PPU_MIRROR_HORIZTAL: /* Horizontal */
  189. ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
  190. ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x000);
  191. ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x400);
  192. ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x400);
  193. break;
  194. case PPU_MIRROR_VERTICAL: /* Vertical */
  195. ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
  196. ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x400);
  197. ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x000);
  198. ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x400);
  199. break;
  200. }
  201. ppu_mirrorMode = direction;
  202. }
  203. void ppu_setSingleScreen(uint8_t screen)
  204. {
  205. if ( ppu_screenMode != PPU_SCMODE_SINGLE )
  206. return;
  207. if ( ppu_singleScreenMode == screen )
  208. return; /* Same value, no need to change! */
  209. switch(screen)
  210. {
  211. default:
  212. screen = PPU_SCREEN_000;
  213. ppu_singleScreenMode = screen;
  214. case PPU_SCREEN_000: /* 0x2000 */
  215. ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
  216. ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x000);
  217. ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x000);
  218. ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x000);
  219. break;
  220. case PPU_SCREEN_400: /* 0x2400 */
  221. ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x400);
  222. ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x400);
  223. ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x400);
  224. ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x400);
  225. break;
  226. case PPU_SCREEN_800: /* 0x2800 */
  227. ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x800);
  228. ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x800);
  229. ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x800);
  230. ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x800);
  231. break;
  232. case PPU_SCREEN_C00: /* 0x2C00 */
  233. ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0xC00);
  234. ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0xC00);
  235. ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0xC00);
  236. ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0xC00);
  237. break;
  238. }
  239. ppu_singleScreenMode = screen;
  240. }
  241. /* Let set display to
  242. Single screen (1 NT with mirroring)
  243. Normal screen (2 NT with mirroring)
  244. Four screen (4 NT without mirroring) */
  245. void ppu_setScreenMode(uint8_t mode)
  246. {
  247. if ( ppu_screenMode == mode )
  248. return; /* Same value, no need to change! */
  249. ppu_screenMode = mode;
  250. switch(mode)
  251. {
  252. case PPU_SCMODE_SINGLE: /* Single screen (1 NT with mirroring) */
  253. ppu_setSingleScreen(~ppu_singleScreenMode);
  254. break;
  255. default:
  256. mode = PPU_SCMODE_NORMAL;
  257. ppu_screenMode = mode;
  258. case PPU_SCMODE_NORMAL: /* Normal screen (2 NT with mirroring) */
  259. ppu_setMirroring(~ppu_mirrorMode);
  260. break;
  261. case PPU_SCMODE_FOURSC: /* Four screen (4 NT withou mirroring) */
  262. ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
  263. ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x400);
  264. ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x800);
  265. ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0xC00);
  266. break;
  267. }
  268. }
  269. /* update whole counters */
  270. void ppu_updateCounters()
  271. {
  272. /*
  273. +---------------+-----------------------------------------------+
  274. | |+===++=++=++=====++=====++===++=++========++==+|
  275. |PPU registers || FV||V||H|| VT|| HT|| FH||S|| PAR||AR||
  276. |PPU counters |+---++-++-++-----++-----++===++=++========++==+|
  277. | |+===++=++=++=====++=====+ |
  278. +---------------+-----------------------------------------------+
  279. |2007 access | DC B A 98765 43210 |
  280. +===============+===============================================+
  281. 8421 8421 8421 8421
  282. -------------------
  283. 1111 1100 0000 0000
  284. 5432 1098 7654 3210
  285. _AAA BCDD DDDE EEEE
  286. */
  287. PPU_Reg_Counter = ( PPU_Reg_FV & 0x07 ) << 12;
  288. PPU_Reg_Counter |= PPU_Reg_V << 11;
  289. PPU_Reg_Counter |= PPU_Reg_H << 10;
  290. PPU_Reg_Counter |= PPU_Reg_VT << 5;
  291. PPU_Reg_Counter |= PPU_Reg_HT;
  292. }
  293. int ppu_hblank(uint16_t scanline)
  294. {
  295. uint32_t i, j;
  296. uint8_t pixelColor = 0;
  297. uint8_t BgColor = 0;
  298. uint8_t SpriteColor = 0;
  299. /* Sprite to display on current scanline */
  300. spriteData scanSprites[8];
  301. uint16_t addr;
  302. uint8_t value;
  303. uint16_t tmp_HHT = 0;
  304. uint16_t tmp_VVTFV = 0;
  305. uint8_t spriteCount = 0;
  306. ppu_scanlineSpriteOverflow = 0;
  307. if ( scanline == 0 )
  308. {
  309. if ( ( ppu_spriteVisibility != 0 ) || ( ppu_backgroundVisibility != 0 ) )
  310. ppu_updateCounters();
  311. }
  312. if ( scanline < 240 )
  313. {
  314. /* Search for sprites on current scanline */
  315. for (i = 0 ; i < 8 ; i++)
  316. {
  317. scanSprites[i].inUse = 0;
  318. }
  319. for (i = 0 ; i < 64 && spriteCount < 8; i++)
  320. {
  321. uint8_t spriteY = ppu_mem_spritesTable[i*4] + 1;
  322. if ((scanline >= spriteY) && (scanline < (spriteY + ppu_spriteSize)))
  323. {
  324. /* This sprite is on the scanline */
  325. scanSprites[spriteCount].inUse = 1;
  326. scanSprites[spriteCount].x = ppu_mem_spritesTable[i*4 + 3];
  327. scanSprites[spriteCount].y = spriteY;
  328. scanSprites[spriteCount].rel_y = scanline - scanSprites[spriteCount].y;
  329. scanSprites[spriteCount].tile = ppu_mem_spritesTable[i * 4 + 1];
  330. scanSprites[spriteCount].bank = ppu_mem_spritesTable[i * 4 + 1] & 0x01;
  331. scanSprites[spriteCount].flip_h = ( ppu_mem_spritesTable[i * 4 + 2] & PPU_SPRITE_FLAGS_HFLIP )?1:0;
  332. scanSprites[spriteCount].flip_v = ( ppu_mem_spritesTable[i * 4 + 2] & PPU_SPRITE_FLAGS_VFLIP )?1:0;
  333. scanSprites[spriteCount].palette = ( ppu_mem_spritesTable[i * 4 + 2] & PPU_SPRITE_FLAGS_UPPERCOLOR ) & 0x0F;
  334. scanSprites[spriteCount].priority = ( ppu_mem_spritesTable[i * 4 + 2] & PPU_SPRITE_FLAGS_BGPRIO )?1:0;
  335. spriteCount++;
  336. }
  337. }
  338. ppu_bgColor = ppu_readMemory(0x3F, 00);
  339. /* For each PPU pixel of this scanline */
  340. for ( i = 0 ; i < 256 ; i++ )
  341. {
  342. /* Set the current pixel color to the bg color */
  343. pixelColor = ppu_bgColor;
  344. BgColor = 0;
  345. /* Compute current pixel bg color if bg is visible */
  346. if (( ppu_backgroundVisibility == 1 ) && (!getKeyStatus('B')))
  347. {
  348. if ( ((i < 8) && (!ppu_backgroundClipping)) || (i >= 8))
  349. {
  350. addr = ( PPU_Reg_Counter & 0x0C00 );
  351. addr = addr | 0x03C0;
  352. addr |= ( PPU_Reg_Counter >> 4 ) & 0x0038;
  353. addr |= ( PPU_Reg_Counter >> 2 ) & 0x0007;
  354. PPU_Reg_AR = ppu_readMemory(0x20 | ( ( addr >> 8 ) & 0x0F ), addr & 0xFF);
  355. PPU_Reg_AR = PPU_Reg_AR >> ( ( ( PPU_Reg_Counter >> 4 ) & 0x04 ) | ( ( PPU_Reg_Counter ) & 0x02 ) );
  356. PPU_Reg_AR = ( PPU_Reg_AR << 2 ) & 0x0C;
  357. PPU_Reg_PAR = ppu_readMemory(0x20 | ( ( PPU_Reg_Counter >> 8 ) & 0x0F ), PPU_Reg_Counter & 0xFF);
  358. addr = PPU_Reg_S;
  359. addr |= ( ( PPU_Reg_PAR & 0xFF ) << 4 );
  360. addr |= ( ( PPU_Reg_Counter >> 12 ) & 0x07 );
  361. value = ppu_readMemory(( addr >> 8 ), addr);
  362. BgColor = ( value & ( 1 << ( 7 - ( i + PPU_Reg_FH ) % 8 ) ) )?0x01:0;
  363. value = ppu_readMemory(( addr >> 8 ), addr | 0x08);
  364. BgColor |= ( value & ( 1 << ( 7 - ( i + PPU_Reg_FH ) % 8 ) ) )?0x02:0;
  365. if ( BgColor > 0x00 )
  366. {
  367. BgColor |= PPU_Reg_AR;
  368. BgColor &= 0x0F;
  369. pixelColor = ppu_readMemory(0x3F, BgColor);
  370. }
  371. if ( ( ( i + PPU_Reg_FH ) % 8 ) == 7 )
  372. {
  373. tmp_HHT = ( ( PPU_Reg_Counter >> 5 ) & 0x0020 ) |
  374. ( PPU_Reg_Counter & 0x001F );
  375. tmp_HHT = ( tmp_HHT + 1 ) & 0x003F;
  376. /* Reassemble with HT & H */
  377. PPU_Reg_Counter = ( PPU_Reg_Counter & 0xFBE0 ) |
  378. ( ( tmp_HHT & 0x0020 ) << 5 ) |
  379. ( tmp_HHT & 0x001F );
  380. }
  381. }
  382. #if 0
  383. // ISPAL
  384. else
  385. {
  386. pixelColor = 0x1D;
  387. }
  388. #endif
  389. }
  390. /* Now calculate if there is a sprite here and sprite visibility is on */
  391. if ( ppu_spriteVisibility == 1 )
  392. {
  393. if (((!ppu_spriteClipping) && (i < 8)) || (i >= 8))
  394. {
  395. for( j = 0 ; j < 8 ; j++)
  396. {
  397. spriteData *sprite = &scanSprites[j];
  398. int8_t spriteRelX;
  399. if ( sprite->inUse == 0 )
  400. break;
  401. spriteRelX = i - sprite->x;
  402. if ( ( spriteRelX >= 0 ) && ( spriteRelX < 8 ) )
  403. {
  404. /* Get sprite tile address */
  405. if ( ppu_spriteSize == 8 )
  406. {
  407. addr = ( sprite->tile << 4 ) + ppu_spritePatternTable;
  408. }
  409. else
  410. {
  411. if ( sprite->rel_y < 8 )
  412. {
  413. addr = ( ( ( sprite->tile & 0xFE ) + ( sprite->flip_v?1:0 ) ) << 4 ) +
  414. ( ( sprite->bank )?0x1000:0x0000 );
  415. }
  416. else
  417. {
  418. addr = ( ( ( sprite->tile & 0xFE ) + ( sprite->flip_v?0:1 ) ) << 4 ) +
  419. ( ( sprite->bank )?0x1000:0x0000 );
  420. }
  421. }
  422. if ( sprite->flip_v )
  423. {
  424. addr += 7;
  425. addr -= sprite->rel_y % 8;
  426. }
  427. else
  428. {
  429. addr += sprite->rel_y % 8;
  430. }
  431. if ( sprite->flip_h )
  432. {
  433. value = ppu_readMemory(( addr >> 8 ), addr);
  434. SpriteColor = ( value & ( 1 << ( spriteRelX ) ) )?0x01:0;
  435. value = ppu_readMemory(( addr >> 8 ), addr | 0x08);
  436. SpriteColor |= ( value & ( 1 << ( spriteRelX ) ) )?0x02:0;
  437. }
  438. else
  439. {
  440. value = ppu_readMemory(( addr >> 8 ), addr);
  441. SpriteColor = ( value & ( 1 << ( 7 - ( spriteRelX ) ) ) )?0x01:0;
  442. value = ppu_readMemory(( addr >> 8 ), addr | 0x08);
  443. SpriteColor |= ( value & ( 1 << ( 7 - ( spriteRelX ) ) ) )?0x02:0;
  444. }
  445. /* If we get a color different from 0, the pixel is not transparent */
  446. if ( SpriteColor > 0 )
  447. {
  448. /* Add second part of the colour */
  449. SpriteColor |= ( ( sprite->palette ) << 2 );
  450. SpriteColor &= 0x0F;
  451. if ( j == 0 )
  452. {
  453. /* Sprite 0 */
  454. if ( ( BgColor != 0 ) && ( !ppu_spriteZeroHit ) )
  455. {
  456. ppu_spriteZeroHit = ( ppu_backgroundVisibility )?1:0;
  457. if ( ppu_spriteZeroHit )
  458. SZHit = scanline;
  459. }
  460. }
  461. if ( sprite->priority )
  462. {
  463. if ( SpriteColor > 0x00 )
  464. {
  465. if ( BgColor == 0 )
  466. {
  467. pixelColor = ppu_readMemory(0x3F, ( 0x10 + SpriteColor ));
  468. }
  469. break;
  470. }
  471. }
  472. else
  473. {
  474. if ( SpriteColor != 0x00 )
  475. {
  476. pixelColor = ppu_readMemory(0x3F, ( 0x10 + SpriteColor ));
  477. break;
  478. }
  479. }
  480. }
  481. }
  482. }
  483. }
  484. }
  485. /* Set to monochrome if needed */
  486. if ( ppu_displayType )
  487. pixelColor &= 0x30;
  488. /* draw the pixel */
  489. graphics_drawpixel(i, scanline, pixelColor);
  490. }
  491. if (spriteCount > 8)
  492. {
  493. ppu_scanlineSpriteOverflow = 1;
  494. }
  495. if ( ppu_backgroundVisibility == 1 )
  496. {
  497. tmp_VVTFV = ( ( PPU_Reg_Counter >> 3 ) & 0x0100 ) | /* V */
  498. ( ( PPU_Reg_Counter >> 2 ) & 0x00F8 ) | /* VT */
  499. ( ( PPU_Reg_Counter >> 12 ) & 0x0007 ); /* FV */
  500. tmp_VVTFV++;
  501. if ( ( tmp_VVTFV & 0x0F8 ) == 0xF0 )
  502. {
  503. tmp_VVTFV &= ~0x0F8;
  504. tmp_VVTFV ^= 0x100;
  505. }
  506. PPU_Reg_Counter = ( PPU_Reg_Counter & 0x041F ) |
  507. ( ( tmp_VVTFV & 0x0100 ) << 3 ) | /* V */
  508. ( ( tmp_VVTFV & 0x00F8 ) << 2 ) | /* VT */
  509. ( ( tmp_VVTFV & 0x0007 ) << 12 ); /* FV */
  510. /* Update H & HT */
  511. PPU_Reg_Counter = ( PPU_Reg_Counter & ~0x041F ) |
  512. ( PPU_Reg_H << 10 ) |
  513. PPU_Reg_HT;
  514. }
  515. }
  516. /* Increment only V, VT & FV*/
  517. /*
  518. 8421 8421 8421 8421
  519. -------------------
  520. 1111 1100 0000 0000
  521. 5432 1098 7654 3210
  522. _AAA BCDD DDDE EEEE
  523. xxx x xx xxx : vvtfv = 7BE0
  524. x x xxxx : hht
  525. B DDDD DAAA : vvtfv
  526. CE EEEE : hht
  527. A = FV
  528. B = V
  529. C = H
  530. D = VT
  531. E = HT
  532. */
  533. if ( scanline == 239 )
  534. {
  535. ppu_inVBlankTime = 1;
  536. // textprintf_ex(Buffer, font, 260, 3, 4, 0, "FPS : %ld (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));
  537. graphics_blit(0, 0, 256, 240);
  538. return ppu_execNMIonVBlank;
  539. }
  540. /* Debug tools */
  541. /*if ( scanline == SZHit )
  542. {
  543. graphics_drawline(0, scanline, 256, scanline, 0x12);
  544. }
  545. if ( scanline == IRQScanHit )
  546. {
  547. graphics_drawline(0, scanline, 256, scanline, 0x13);
  548. }*/
  549. /* */
  550. if ( scanline == ( 239 + VBLANK_TIME ) + 0 )
  551. {
  552. ppu_inVBlankTime = 0;
  553. ppu_spriteZeroHit = 0;
  554. ppu_scanlineSpriteOverflow = 0;
  555. }
  556. return 0;
  557. }
  558. uint8_t PPU_RegValues[8];
  559. uint8_t ppu_readReg(uint8_t id)
  560. {
  561. id &= 0x07;
  562. static uint8_t garbage;
  563. static uint8_t lastValue;
  564. switch(id)
  565. {
  566. default:
  567. garbage = PPU_RegValues[id];
  568. break;
  569. case 0x02: /* PPU Status Register */
  570. /* Reset VRam 2005/2006 flipflop */
  571. ppu_VramAccessFlipFlop = 0;
  572. garbage = 0;
  573. garbage |= ( ppu_inVBlankTime != 0 )?PPU_FLAG_SR_VBLANK:0;
  574. garbage |= ( ppu_spriteZeroHit != 0 )?PPU_FLAG_SR_SPRT0:0;
  575. garbage |= ( ppu_scanlineSpriteOverflow != 0 )?PPU_FLAG_SR_8SPRT:0;
  576. ppu_inVBlankTime = 0;
  577. break;
  578. case 0x04: /* SPR-RAM I/O */
  579. garbage = ppu_mem_spritesTable[ppu_mem_sptrTablePtr];
  580. break;
  581. case 0x07: /* VRAM I/O */
  582. if ( PPU_Reg_Counter < 0x3F00 )
  583. {
  584. garbage = lastValue;
  585. lastValue = ppu_readMemory(( PPU_Reg_Counter >> 8 ) & 0x3F,
  586. PPU_Reg_Counter & 0xFF);
  587. }
  588. else
  589. {
  590. lastValue = ppu_readMemory(0x2F,
  591. PPU_Reg_Counter & 0xFF);
  592. garbage = ppu_readMemory(0x3F,
  593. PPU_Reg_Counter & 0xFF);
  594. }
  595. PPU_Reg_Counter += ppu_addrIncrement;
  596. break;
  597. }
  598. return garbage;
  599. }
  600. void ppu_writeReg(uint8_t id, uint8_t val)
  601. {
  602. id &= 0x07;
  603. PPU_RegValues[id] = val;
  604. switch(id)
  605. {
  606. default:
  607. break;
  608. case 0x00: /* PPU Control Register #1 */
  609. /*
  610. +===============+===============================================+
  611. |2000 | 1 0 4 |
  612. +---------------+-----------------------------------------------+
  613. | |+===++=++=++=====++=====++===++=++========++==+|
  614. |PPU registers || FV||V||H|| VT|| HT|| FH||S|| PAR||AR||
  615. |PPU counters |+---++-++-++-----++-----++===++=++========++==+|
  616. | |+===++=++=++=====++=====+ |
  617. +---------------+-----------------------------------------------+
  618. */
  619. /* Set PPU internal registers */
  620. PPU_Reg_V = ( val & 0x02 )?1:0;
  621. PPU_Reg_H = ( val & 0x01 )?1:0;
  622. PPU_Reg_S = ( val & 0x10 )?0x1000:0x0000;
  623. /* Set Other parameters */
  624. ppu_addrIncrement = ( val & 0x04 )?0x20:0x01;
  625. ppu_spritePatternTable = ( val & 0x08 )?0x1000:0;
  626. ppu_spriteSize = ( val & 0x20 )?16:8;
  627. ppu_execNMIonVBlank = ( val & 0x80 )?1:0;
  628. break;
  629. case 0x01: /* PPU Control Register #2 */
  630. ppu_spriteVisibility = ( val & 0x10 )?1:0;
  631. ppu_backgroundVisibility = ( val & 0x08 )?1:0;
  632. ppu_spriteClipping = ( val & 0x04 )?0:1;
  633. ppu_backgroundClipping = ( val & 0x02 )?0:1;
  634. ppu_displayType = ( val & 0x01 )?1:0;
  635. break;
  636. case 0x03: /* SPR-RAM Address Register */
  637. ppu_mem_sptrTablePtr = val;
  638. break;
  639. case 0x04: /* SPR-RAM I/O */
  640. ppu_mem_spritesTable[ppu_mem_sptrTablePtr++] = val;
  641. break;
  642. case 0x05: /* 2005 VRAM Register */
  643. /*
  644. +===============+===============================================+
  645. |2005/1 | 76543 210 |
  646. |2005/2 | 210 76543 |
  647. +---------------+-----------------------------------------------+
  648. | |+===++=++=++=====++=====++===++=++========++==+|
  649. |PPU registers || FV||V||H|| VT|| HT|| FH||S|| PAR||AR||
  650. |PPU counters |+---++-++-++-----++-----++===++=++========++==+|
  651. | |+===++=++=++=====++=====+ |
  652. +---------------+-----------------------------------------------+
  653. */
  654. if ( ppu_VramAccessFlipFlop == 0 )
  655. {
  656. ppu_VramAccessFlipFlop = ~0;
  657. PPU_Reg_FH = val & 0x07;
  658. PPU_Reg_HT = ( val & 0xF8 ) >> 3;
  659. }
  660. else
  661. {
  662. ppu_VramAccessFlipFlop = 0;
  663. PPU_Reg_FV = val & 0x07;
  664. PPU_Reg_VT = ( val & 0xF8 ) >> 3;
  665. }
  666. break;
  667. case 0x06: /* 2006 VRAM Register */
  668. /*
  669. +===============+===============================================+
  670. |2006/1 | -54 3 2 10 |
  671. |2006/2 | 765 43210 |
  672. +---------------+-----------------------------------------------+
  673. | |+===++=++=++=====++=====++===++=++========++==+|
  674. |PPU registers || FV||V||H|| VT|| HT|| FH||S|| PAR||AR||
  675. |PPU counters |+---++-++-++-----++-----++===++=++========++==+|
  676. | |+===++=++=++=====++=====+ |
  677. +---------------+-----------------------------------------------+
  678. */
  679. if ( ppu_VramAccessFlipFlop == 0 )
  680. {
  681. ppu_VramAccessFlipFlop = ~0;
  682. PPU_Reg_FV = ( val >> 4 ) & 0x03;
  683. PPU_Reg_V = ( val >> 3 ) & 0x01;
  684. PPU_Reg_H = ( val >> 2 ) & 0x01;
  685. PPU_Reg_VT = ( PPU_Reg_VT & 0x07 ) | ( ( val & 0x03 ) << 3 );
  686. }
  687. else
  688. {
  689. ppu_VramAccessFlipFlop = 0;
  690. PPU_Reg_VT = ( PPU_Reg_VT & 0x18 ) | ( ( val >> 5 ) & 0x07 );
  691. PPU_Reg_HT = val & 0x1F;
  692. ppu_updateCounters();
  693. }
  694. break;
  695. case 0x07: /* VRAM I/O */
  696. /*
  697. +---------------+-----------------------------------------------+
  698. | |+===++=++=++=====++=====++===++=++========++==+|
  699. |PPU registers || FV||V||H|| VT|| HT|| FH||S|| PAR||AR||
  700. |PPU counters |+---++-++-++-----++-----++===++=++========++==+|
  701. | |+===++=++=++=====++=====+ |
  702. +---------------+-----------------------------------------------+
  703. |2007 access | DC B A 98765 43210 |
  704. +===============+===============================================+
  705. */
  706. ppu_writeMemory(( PPU_Reg_Counter >> 8 ) & 0x3F, PPU_Reg_Counter & 0xFF, val);
  707. PPU_Reg_Counter += ppu_addrIncrement;
  708. break;
  709. }
  710. }
  711. void ppu_fillSprRamDMA(uint8_t value)
  712. {
  713. short i;
  714. uint8_t *ptr = get_page_ptr(value);
  715. for ( i = 0 ; i < 0x100 ; i++ )
  716. {
  717. ppu_mem_spritesTable[( ppu_mem_sptrTablePtr + i ) & 0xFF] = *( ptr + i );
  718. }
  719. }