ppu.c 35 KB

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