ppu.c 30 KB

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