mode4.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. /*
  2. * SMS renderer
  3. * (C) notaz, 2009-2010
  4. * (C) kub, 2021
  5. *
  6. * currently supports VDP mode 4 (SMS and GG) and mode 2+0 (TMS)
  7. *
  8. * This work is licensed under the terms of MAME license.
  9. * See COPYING file in the top-level directory.
  10. */
  11. /*
  12. * TODO:
  13. * - other TMS9918 modes?
  14. */
  15. #include "pico_int.h"
  16. #include <platform/common/upscale.h>
  17. static void (*FinalizeLineSMS)(int line);
  18. static int skip_next_line;
  19. static int screen_offset, line_offset;
  20. static u8 mode;
  21. static unsigned int sprites_addr[32]; // bitmap address
  22. static unsigned char sprites_c[32]; // TMS sprites color
  23. static int sprites_x[32]; // x position
  24. static int sprites; // count
  25. static unsigned char sprites_map[2+256/8+2]; // collision detection map
  26. unsigned int sprites_status;
  27. /* sprite collision detection */
  28. static int CollisionDetect(u8 *mb, u16 sx, unsigned int pack, int zoomed)
  29. {
  30. static u8 morton[16] = { 0x00,0x03,0x0c,0x0f,0x30,0x33,0x3c,0x3f,
  31. 0xc0,0xc3,0xcc,0xcf,0xf0,0xf3,0xfc,0xff };
  32. u8 *mp = mb + (sx>>3);
  33. unsigned col, m;
  34. // check sprite map for collision and update map with current sprite
  35. if (!zoomed) { // 8 sprite pixels
  36. m = mp[0] | (mp[1]<<8);
  37. col = m & (pack<<(sx&7)); // collision if current sprite overlaps sprite map
  38. m |= pack<<(sx&7);
  39. mp[0] = m, mp[1] = m>>8;
  40. } else { // 16 sprite pixels in zoom mode
  41. pack = morton[pack&0x0f] | (morton[(pack>>4)&0x0f] << 8);
  42. m = mp[0] | (mp[1]<<8) | (mp[2]<<16);
  43. col = m & (pack<<(sx&7));
  44. m |= pack<<(sx&7);
  45. mp[0] = m, mp[1] = m>>8, mp[2] = m>>16;
  46. }
  47. // invisible overscan area, not tested for collision
  48. mb[0] = mb[33] = mb[34] = 0;
  49. return col;
  50. }
  51. /* Mode 4 */
  52. /*========*/
  53. static void TileBGM4(u16 sx, int pal)
  54. {
  55. if (sx & 3) {
  56. u8 *pd = (u8 *)(Pico.est.HighCol + sx);
  57. pd[0] = pd[1] = pd[2] = pd[3] = pal;
  58. pd[4] = pd[5] = pd[6] = pd[7] = pal;
  59. } else {
  60. u32 *pd = (u32 *)(Pico.est.HighCol + sx);
  61. pd[0] = pd[1] = pal * 0x01010101;
  62. }
  63. }
  64. // 8 pixels are arranged in 4 bitplane bytes in a 32 bit word. To pull the
  65. // 4 bitplanes together multiply with each bit distance (multiples of 1<<7)
  66. #define PLANAR_PIXELBG(x,p) \
  67. t = (pack>>(7-p)) & 0x01010101; \
  68. t = (t*0x10204080) >> 28; \
  69. pd[x] = pal|t;
  70. static void TileNormBGM4(u16 sx, unsigned int pack, int pal)
  71. {
  72. u8 *pd = Pico.est.HighCol + sx;
  73. u32 t;
  74. PLANAR_PIXELBG(0, 0)
  75. PLANAR_PIXELBG(1, 1)
  76. PLANAR_PIXELBG(2, 2)
  77. PLANAR_PIXELBG(3, 3)
  78. PLANAR_PIXELBG(4, 4)
  79. PLANAR_PIXELBG(5, 5)
  80. PLANAR_PIXELBG(6, 6)
  81. PLANAR_PIXELBG(7, 7)
  82. }
  83. static void TileFlipBGM4(u16 sx, unsigned int pack, int pal)
  84. {
  85. u8 *pd = Pico.est.HighCol + sx;
  86. u32 t;
  87. PLANAR_PIXELBG(0, 7)
  88. PLANAR_PIXELBG(1, 6)
  89. PLANAR_PIXELBG(2, 5)
  90. PLANAR_PIXELBG(3, 4)
  91. PLANAR_PIXELBG(4, 3)
  92. PLANAR_PIXELBG(5, 2)
  93. PLANAR_PIXELBG(6, 1)
  94. PLANAR_PIXELBG(7, 0)
  95. }
  96. // non-transparent sprite pixels apply if no higher prio pixel is already there
  97. #define PLANAR_PIXELSP(x,p) \
  98. t = (pack>>(7-p)) & 0x01010101; \
  99. if (t && (pd[x] & 0x2f) <= 0x20) { \
  100. t = (t*0x10204080) >> 28; \
  101. pd[x] = pal|t; \
  102. }
  103. static void TileNormSprM4(u16 sx, unsigned int pack, int pal)
  104. {
  105. u8 *pd = Pico.est.HighCol + sx;
  106. u32 t;
  107. PLANAR_PIXELSP(0, 0)
  108. PLANAR_PIXELSP(1, 1)
  109. PLANAR_PIXELSP(2, 2)
  110. PLANAR_PIXELSP(3, 3)
  111. PLANAR_PIXELSP(4, 4)
  112. PLANAR_PIXELSP(5, 5)
  113. PLANAR_PIXELSP(6, 6)
  114. PLANAR_PIXELSP(7, 7)
  115. }
  116. static void TileDoubleSprM4(int sx, unsigned int pack, int pal)
  117. {
  118. u8 *pd = Pico.est.HighCol + sx;
  119. u32 t;
  120. PLANAR_PIXELSP(0, 0)
  121. PLANAR_PIXELSP(1, 0)
  122. PLANAR_PIXELSP(2, 1)
  123. PLANAR_PIXELSP(3, 1)
  124. PLANAR_PIXELSP(4, 2)
  125. PLANAR_PIXELSP(5, 2)
  126. PLANAR_PIXELSP(6, 3)
  127. PLANAR_PIXELSP(7, 3)
  128. PLANAR_PIXELSP(8, 4)
  129. PLANAR_PIXELSP(9, 4)
  130. PLANAR_PIXELSP(10, 5)
  131. PLANAR_PIXELSP(11, 5)
  132. PLANAR_PIXELSP(12, 6)
  133. PLANAR_PIXELSP(13, 6)
  134. PLANAR_PIXELSP(14, 7)
  135. PLANAR_PIXELSP(15, 7)
  136. }
  137. static void ParseSpritesM4(int scanline)
  138. {
  139. struct PicoVideo *pv = &Pico.video;
  140. u8 *sat;
  141. int xoff = 8; // relative to HighCol, which is (screen - 8)
  142. int sprite_base, addr_mask;
  143. int zoomed = pv->reg[1] & 0x1; // zoomed sprites, e.g. Earthworm Jim
  144. unsigned int pack;
  145. int i, s, h, m;
  146. if (pv->reg[0] & 8)
  147. xoff = 0;
  148. xoff += line_offset;
  149. if ((Pico.m.hardware & 0x3) == 0x3)
  150. xoff -= 48; // GG LCD, adjust to center 160 px
  151. sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
  152. if (pv->reg[1] & 2) {
  153. addr_mask = 0xfe; h = 16;
  154. } else {
  155. addr_mask = 0xff; h = 8;
  156. }
  157. if (zoomed) h *= 2;
  158. sprite_base = (pv->reg[6] & 4) << (13-2-1);
  159. m = 0;
  160. memset(sprites_map, 0, sizeof(sprites_map));
  161. for (i = s = 0; i < 64; i++)
  162. {
  163. int y;
  164. y = sat[MEM_LE2(i)];
  165. if (y == 0xd0 && !((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)))
  166. break;
  167. if (y >= 0xe0)
  168. y -= 256;
  169. y &= ~zoomed; // zoomed sprites apparently only on even lines, see GG Tarzan
  170. if (y + h <= scanline || scanline < y)
  171. continue; // not on this line
  172. if (s >= 8) {
  173. if (scanline >= 0) sprites_status |= SR_SOVR;
  174. if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32)
  175. break;
  176. }
  177. if (xoff + sat[MEM_LE2(0x80 + i*2)] >= 0) {
  178. sprites_x[s] = xoff + sat[MEM_LE2(0x80 + i*2)];
  179. sprites_addr[s] = sprite_base + ((sat[MEM_LE2(0x80 + i*2 + 1)] & addr_mask) << (5-1)) +
  180. ((scanline - y) >> zoomed << (2-1));
  181. if (Pico.video.reg[1] & 0x40) {
  182. // collision detection. Do it here since off-screen lines aren't drawn
  183. pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s]));
  184. // make sprite pixel map by merging the 4 bitplanes
  185. pack = ((pack | (pack>>16)) | ((pack | (pack>>16))>>8)) & 0xff;
  186. if (!m) m = CollisionDetect(sprites_map, sprites_x[s], pack, zoomed);
  187. }
  188. s++;
  189. }
  190. }
  191. if (m)
  192. sprites_status |= SR_C;
  193. sprites = s;
  194. }
  195. static void DrawSpritesM4(void)
  196. {
  197. struct PicoVideo *pv = &Pico.video;
  198. unsigned int pack;
  199. int zoomed = pv->reg[1] & 0x1; // zoomed sprites, e.g. Earthworm Jim
  200. int s = sprites;
  201. // now draw all sprites backwards
  202. for (--s; s >= 0; s--) {
  203. pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s]));
  204. if (zoomed) TileDoubleSprM4(sprites_x[s], pack, 0x10);
  205. else TileNormSprM4(sprites_x[s], pack, 0x10);
  206. }
  207. }
  208. // cells_dx, tilex_ty merged to reduce register pressure
  209. static void DrawStripM4(const u16 *nametab, int cells_dx, int tilex_ty)
  210. {
  211. int oldcode = -1;
  212. int addr = 0, pal = 0;
  213. // Draw tiles across screen:
  214. for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
  215. {
  216. unsigned int pack;
  217. unsigned code;
  218. code = nametab[tilex_ty & 0x1f];
  219. if (code != oldcode) {
  220. oldcode = code;
  221. // Get tile address/2:
  222. addr = (code & 0x1ff) << 4;
  223. addr += tilex_ty >> 16;
  224. if (code & 0x0400)
  225. addr ^= 0xe; // Y-flip
  226. pal = (code>>7) & 0x30; // prio | palette select
  227. }
  228. pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); // Get 4 bitplanes / 8 pixels
  229. if (pack == 0) TileBGM4(cells_dx, pal);
  230. else if (code & 0x0200) TileFlipBGM4(cells_dx, pack, pal);
  231. else TileNormBGM4(cells_dx, pack, pal);
  232. }
  233. }
  234. static void DrawDisplayM4(int scanline)
  235. {
  236. struct PicoVideo *pv = &Pico.video;
  237. u16 *nametab, *nametab2;
  238. int line, tilex, dx, ty, cells;
  239. int cellskip = 0; // XXX
  240. int maxcells = 32;
  241. // Find the line in the name table
  242. line = pv->reg[9] + scanline; // vscroll + scanline
  243. // Find name table:
  244. nametab = PicoMem.vram;
  245. if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) {
  246. // 224/240 line mode
  247. line &= 0xff;
  248. nametab += ((pv->reg[2] & 0x0c) << (10-1)) + (0x700 >> 1);
  249. } else {
  250. while (line >= 224) line -= 224;
  251. nametab += (pv->reg[2] & 0x0e) << (10-1);
  252. // old SMS only, masks line:7 with reg[2]:0 for address calculation
  253. //if ((pv->reg[2] & 0x01) == 0) line &= 0x7f;
  254. }
  255. nametab2 = nametab + ((scanline>>3) << (6-1));
  256. nametab = nametab + ((line>>3) << (6-1));
  257. dx = pv->reg[8]; // hscroll
  258. if (scanline < 16 && (pv->reg[0] & 0x40))
  259. dx = 0; // hscroll disabled for top 2 rows (e.g. Fantasy Zone II)
  260. tilex = ((-dx >> 3) + cellskip) & 0x1f;
  261. ty = (line & 7) << 1; // Y-Offset into tile
  262. cells = maxcells - cellskip;
  263. dx = ((dx - 1) & 7) + 1;
  264. if (dx != 8)
  265. cells++; // have hscroll, need to draw 1 cell more
  266. dx += cellskip << 3;
  267. dx += line_offset;
  268. // tiles
  269. if (!(pv->debug_p & PVD_KILL_B)) {
  270. if ((Pico.m.hardware & 0x3) == 0x3) {
  271. // on GG render only the center 160 px
  272. DrawStripM4(nametab , dx | ((cells-12)<< 16),(tilex+6) | (ty << 16));
  273. } else if (pv->reg[0] & 0x80) {
  274. // vscroll disabled for rightmost 8 columns (e.g. Gauntlet)
  275. int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline&7;
  276. DrawStripM4(nametab, dx | ((cells-8) << 16), tilex | (ty << 16));
  277. DrawStripM4(nametab2, dx2 | (8 << 16), tilex2 | (ty2 << 17));
  278. } else
  279. DrawStripM4(nametab , dx | ( cells << 16), tilex | (ty << 16));
  280. }
  281. // sprites
  282. if (!(pv->debug_p & PVD_KILL_S_LO))
  283. DrawSpritesM4();
  284. if ((pv->reg[0] & 0x20) && (Pico.m.hardware & 0x3) != 0x3) {
  285. // first column masked with background, caculate offset to start of line
  286. dx = (dx&~0x1f) / 4;
  287. ty = ((pv->reg[7]&0x0f)|0x10) * 0x01010101;
  288. ((u32 *)Pico.est.HighCol)[dx+2] = ((u32 *)Pico.est.HighCol)[dx+3] = ty;
  289. }
  290. }
  291. /* TMS Modes */
  292. /*===========*/
  293. /* Background, Graphics modes */
  294. #define TMS_PIXELBG(x,p) \
  295. t = (pack>>(7-p)) & 0x01; \
  296. t = (pal >> (t << 2)) & 0x0f; \
  297. pd[x] = t;
  298. static void TileNormBgGr(u16 sx, unsigned int pack, int pal)
  299. {
  300. u8 *pd = Pico.est.HighCol + sx;
  301. unsigned int t;
  302. TMS_PIXELBG(0, 0)
  303. TMS_PIXELBG(1, 1)
  304. TMS_PIXELBG(2, 2)
  305. TMS_PIXELBG(3, 3)
  306. TMS_PIXELBG(4, 4)
  307. TMS_PIXELBG(5, 5)
  308. TMS_PIXELBG(6, 6)
  309. TMS_PIXELBG(7, 7)
  310. }
  311. /* Sprites */
  312. #define TMS_PIXELSP(x,p) \
  313. t = (pack>>(7-p)) & 0x01; \
  314. if (t) \
  315. pd[x] = pal;
  316. static void TileNormSprTMS(u16 sx, unsigned int pack, int pal)
  317. {
  318. u8 *pd = Pico.est.HighCol + sx;
  319. unsigned int t;
  320. TMS_PIXELSP(0, 0)
  321. TMS_PIXELSP(1, 1)
  322. TMS_PIXELSP(2, 2)
  323. TMS_PIXELSP(3, 3)
  324. TMS_PIXELSP(4, 4)
  325. TMS_PIXELSP(5, 5)
  326. TMS_PIXELSP(6, 6)
  327. TMS_PIXELSP(7, 7)
  328. }
  329. static void TileDoubleSprTMS(u16 sx, unsigned int pack, int pal)
  330. {
  331. u8 *pd = Pico.est.HighCol + sx;
  332. unsigned int t;
  333. TMS_PIXELSP(0, 0)
  334. TMS_PIXELSP(1, 0)
  335. TMS_PIXELSP(2, 1)
  336. TMS_PIXELSP(3, 1)
  337. TMS_PIXELSP(4, 2)
  338. TMS_PIXELSP(5, 2)
  339. TMS_PIXELSP(6, 3)
  340. TMS_PIXELSP(7, 3)
  341. TMS_PIXELSP(8, 4)
  342. TMS_PIXELSP(9, 4)
  343. TMS_PIXELSP(10, 5)
  344. TMS_PIXELSP(11, 5)
  345. TMS_PIXELSP(12, 6)
  346. TMS_PIXELSP(13, 6)
  347. TMS_PIXELSP(14, 7)
  348. TMS_PIXELSP(15, 7)
  349. }
  350. static void ParseSpritesTMS(int scanline)
  351. {
  352. struct PicoVideo *pv = &Pico.video;
  353. unsigned int pack;
  354. u8 *sat;
  355. int xoff = 8; // relative to HighCol, which is (screen - 8)
  356. int sprite_base, addr_mask;
  357. int zoomed = pv->reg[1] & 0x1; // zoomed sprites
  358. int i, s, h, m;
  359. xoff += line_offset;
  360. sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7e) << 7);
  361. if (pv->reg[1] & 2) {
  362. addr_mask = 0xfc; h = 16;
  363. } else {
  364. addr_mask = 0xff; h = 8;
  365. }
  366. if (zoomed) h *= 2;
  367. sprite_base = (pv->reg[6] & 0x7) << 11;
  368. m = 0;
  369. memset(sprites_map, 0, sizeof(sprites_map));
  370. /* find sprites on this scanline */
  371. for (i = s = 0; i < 32; i++)
  372. {
  373. int x, y;
  374. y = sat[MEM_LE2(4*i)];
  375. if (y == 0xd0)
  376. break;
  377. if (y >= 0xe0)
  378. y -= 256;
  379. y &= ~zoomed;
  380. if (y + h <= scanline || scanline < y)
  381. continue; // not on this line
  382. if (s >= 4) {
  383. if (scanline >= 0) sprites_status |= SR_SOVR | i;
  384. if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32)
  385. break;
  386. }
  387. x = sat[MEM_LE2(4*i+1)] + xoff;
  388. if (sat[MEM_LE2(4*i+3)] & 0x80)
  389. x -= 32;
  390. sprites_c[s] = sat[MEM_LE2(4*i+3)] & 0x0f;
  391. sprites_x[s] = x;
  392. sprites_addr[s] = sprite_base + ((sat[MEM_LE2(4*i + 2)] & addr_mask) << 3) +
  393. ((scanline - y) >> zoomed);
  394. if (Pico.video.reg[1] & 0x40) {
  395. // collision detection. Do it here since off-screen lines aren't drawn
  396. if (sprites_c[s] && x > 0) {
  397. pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
  398. if (!m) m = CollisionDetect(sprites_map, x, pack, zoomed);
  399. }
  400. x += (zoomed ? 16:8);
  401. if (sprites_c[s] && (pv->reg[1] & 0x2) && x > 0 && x < 8+256) {
  402. pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
  403. if (!m) m = CollisionDetect(sprites_map, x, pack, zoomed);
  404. }
  405. }
  406. s++;
  407. }
  408. if (m)
  409. sprites_status |= SR_C;
  410. sprites = s;
  411. }
  412. /* Draw sprites into a scanline, max 4 */
  413. static void DrawSpritesTMS(void)
  414. {
  415. struct PicoVideo *pv = &Pico.video;
  416. unsigned int pack;
  417. int zoomed = pv->reg[1] & 0x1; // zoomed sprites
  418. int s = sprites;
  419. // now draw all sprites backwards
  420. for (--s; s >= 0; s--) {
  421. int x, c, w = (zoomed ? 16: 8);
  422. x = sprites_x[s];
  423. c = sprites_c[s];
  424. // c may be 0 (transparent): sprite invisible
  425. if (c && x > 0) {
  426. pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
  427. if (zoomed) TileDoubleSprTMS(x, pack, c);
  428. else TileNormSprTMS(x, pack, c);
  429. }
  430. if (c && (pv->reg[1] & 0x2) && (x+=w) > 0 && x < 8+256) {
  431. pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
  432. if (zoomed) TileDoubleSprTMS(x, pack, c);
  433. else TileNormSprTMS(x, pack, c);
  434. }
  435. }
  436. }
  437. /* Mode 2 */
  438. /*========*/
  439. /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */
  440. static void DrawStripM2(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty)
  441. {
  442. // Draw tiles across screen:
  443. for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
  444. {
  445. unsigned int pack, pal;
  446. unsigned code;
  447. code = nametab[tilex_ty & 0x1f] << 3;
  448. pal = coltab[code];
  449. pack = pattab[code];
  450. TileNormBgGr(cells_dx, pack, pal);
  451. }
  452. }
  453. /* Draw a scanline */
  454. static void DrawDisplayM2(int scanline)
  455. {
  456. struct PicoVideo *pv = &Pico.video;
  457. u8 *nametab, *coltab, *pattab;
  458. int tilex, dx, cells;
  459. int cellskip = 0; // XXX
  460. int maxcells = 32;
  461. // name, color, pattern table:
  462. nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00);
  463. coltab = PicoMem.vramb + ((pv->reg[3]<< 6) & 0x2000);
  464. pattab = PicoMem.vramb + ((pv->reg[4]<<11) & 0x2000);
  465. nametab += ((scanline>>3) << 5);
  466. coltab += ((scanline>>6) <<11) + (scanline & 0x7);
  467. pattab += ((scanline>>6) <<11) + (scanline & 0x7);
  468. tilex = cellskip & 0x1f;
  469. cells = maxcells - cellskip;
  470. dx = (cellskip << 3) + line_offset + 8;
  471. // tiles
  472. if (!(pv->debug_p & PVD_KILL_B))
  473. DrawStripM2(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16));
  474. // sprites
  475. if (!(pv->debug_p & PVD_KILL_S_LO))
  476. DrawSpritesTMS();
  477. }
  478. /* Mode 0 */
  479. /*========*/
  480. /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */
  481. static void DrawStripM0(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty)
  482. {
  483. // Draw tiles across screen:
  484. for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
  485. {
  486. unsigned int pack, pal;
  487. unsigned code;
  488. code = nametab[tilex_ty & 0x1f];
  489. pal = coltab[code >> 3];
  490. pack = pattab[code << 3];
  491. TileNormBgGr(cells_dx, pack, pal);
  492. }
  493. }
  494. /* Draw a scanline */
  495. static void DrawDisplayM0(int scanline)
  496. {
  497. struct PicoVideo *pv = &Pico.video;
  498. u8 *nametab, *coltab, *pattab;
  499. int tilex, dx, cells;
  500. int cellskip = 0; // XXX
  501. int maxcells = 32;
  502. // name, color, pattern table:
  503. nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00);
  504. coltab = PicoMem.vramb + ((pv->reg[3]<< 6) & 0x3fc0);
  505. pattab = PicoMem.vramb + ((pv->reg[4]<<11) & 0x3800);
  506. nametab += (scanline>>3) << 5;
  507. pattab += (scanline & 0x7);
  508. tilex = cellskip & 0x1f;
  509. cells = maxcells - cellskip;
  510. dx = (cellskip << 3) + line_offset + 8;
  511. // tiles
  512. if (!(pv->debug_p & PVD_KILL_B))
  513. DrawStripM0(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16));
  514. // sprites
  515. if (!(pv->debug_p & PVD_KILL_S_LO))
  516. DrawSpritesTMS();
  517. }
  518. /* Common/global */
  519. /*===============*/
  520. static void FinalizeLineRGB555SMS(int line);
  521. static void FinalizeLine8bitSMS(int line);
  522. void PicoFrameStartSMS(void)
  523. {
  524. int lines = 192, columns = 256, loffs, coffs;
  525. skip_next_line = 0;
  526. loffs = screen_offset = 24; // 192 lines is really 224 with top/bottom bars
  527. Pico.est.rendstatus = PDRAW_32_COLS;
  528. // if mode changes make palette dirty since some modes switch to a fixed one
  529. if (mode != ((Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18))) {
  530. mode = (Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18);
  531. Pico.m.dirtyPal = 1;
  532. }
  533. // Copy LCD enable flag for easier handling
  534. Pico.m.hardware &= ~0x2;
  535. if (PicoIn.opt & POPT_EN_GG_LCD)
  536. Pico.m.hardware |= 0x2;
  537. if ((Pico.m.hardware & 0x3) == 0x3) {
  538. // GG LCD always has 160x144 regardless of settings
  539. screen_offset = 24; // nonetheless the vdp timing has 224 lines
  540. loffs = 48;
  541. lines = 144;
  542. columns = 160;
  543. } else switch (mode) {
  544. // SMS2 only 224/240 line modes, e.g. Micro Machines
  545. case 0x06|0x08:
  546. loffs = screen_offset = 0;
  547. lines = 240;
  548. break;
  549. case 0x06|0x10:
  550. loffs = screen_offset = 8;
  551. lines = 224;
  552. break;
  553. }
  554. if (PicoIn.opt & POPT_EN_SOFTSCALE) {
  555. coffs = 0;
  556. columns = 320;
  557. } else
  558. coffs = PicoIn.opt & POPT_DIS_32C_BORDER ? 0:(320-columns)/2;
  559. line_offset = (FinalizeLineSMS == NULL ? coffs : 0);
  560. if (FinalizeLineSMS == FinalizeLineRGB555SMS)
  561. line_offset = 0 /* done in FinalizeLine */;
  562. if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) {
  563. emu_video_mode_change(loffs, lines, coffs, columns);
  564. rendstatus_old = Pico.est.rendstatus;
  565. rendlines = lines;
  566. sprites = 0;
  567. }
  568. Pico.est.HighCol = HighColBase + screen_offset * HighColIncrement;
  569. Pico.est.DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement;
  570. if (FinalizeLineSMS == FinalizeLine8bitSMS) {
  571. Pico.m.dirtyPal = (Pico.m.dirtyPal || Pico.est.SonicPalCount ? 2 : 0);
  572. memcpy(Pico.est.SonicPal, PicoMem.cram, 0x40*2);
  573. }
  574. Pico.est.SonicPalCount = 0;
  575. }
  576. void PicoParseSATSMS(int line)
  577. {
  578. if (Pico.video.reg[0] & 0x04) ParseSpritesM4(line);
  579. else ParseSpritesTMS(line);
  580. }
  581. void PicoLineSMS(int line)
  582. {
  583. int skip = skip_next_line;
  584. unsigned bgcolor;
  585. // GG LCD, render only visible part of screen
  586. if ((Pico.m.hardware & 0x3) == 0x3 && (line < 24 || line >= 24+144))
  587. goto norender;
  588. if (PicoScanBegin != NULL && skip == 0)
  589. skip = PicoScanBegin(line + screen_offset);
  590. if (skip) {
  591. skip_next_line = skip - 1;
  592. return;
  593. }
  594. // Draw screen:
  595. bgcolor = (Pico.video.reg[7] & 0x0f) | ((Pico.video.reg[0] & 0x04) << 2);
  596. BackFill(bgcolor, 0, &Pico.est); // bgcolor is from 2nd palette in mode 4
  597. if (Pico.video.reg[1] & 0x40) {
  598. if (Pico.video.reg[0] & 0x04) DrawDisplayM4(line);
  599. else if (Pico.video.reg[0] & 0x02) DrawDisplayM2(line);
  600. else DrawDisplayM0(line);
  601. }
  602. if (FinalizeLineSMS != NULL)
  603. FinalizeLineSMS(line);
  604. if (PicoScanEnd != NULL)
  605. skip_next_line = PicoScanEnd(line + screen_offset);
  606. norender:
  607. Pico.est.HighCol += HighColIncrement;
  608. Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;
  609. }
  610. /* Fixed palette for TMS9918 modes */
  611. static u16 tmspal[32] = {
  612. // SMS palette
  613. 0x0000, 0x0000, 0x00a0, 0x00f0, 0x0500, 0x0f00, 0x0005, 0x0ff0,
  614. 0x000a, 0x000f, 0x0055, 0x00ff, 0x0050, 0x0f0f, 0x0555, 0x0fff,
  615. // TMS palette
  616. 0x0000, 0x0000, 0x04c2, 0x07d5, 0x0e55, 0x0f77, 0x045d, 0x0fe4,
  617. 0x055f, 0x077f, 0x05cd, 0x08ce, 0x03b2, 0x0b5c, 0x0ccc, 0x0fff,
  618. };
  619. void PicoDoHighPal555SMS(void)
  620. {
  621. u32 *spal = (void *)Pico.est.SonicPal;
  622. u32 *dpal = (void *)Pico.est.HighPal;
  623. unsigned int cnt = Pico.est.SonicPalCount+1;
  624. unsigned int t;
  625. int i, j;
  626. if (FinalizeLineSMS != FinalizeLine8bitSMS || Pico.m.dirtyPal == 2)
  627. Pico.m.dirtyPal = 0;
  628. // use hardware palette for 16bit accurate mode
  629. if (FinalizeLineSMS == FinalizeLineRGB555SMS)
  630. spal = (void *)PicoMem.cram;
  631. /* SMS 6 bit cram data was already converted to MD/GG format by vdp write,
  632. * hence GG/SMS/TMS can all be handled the same here */
  633. for (j = cnt; j > 0; j--) {
  634. if (!(Pico.video.reg[0] & 0x4))
  635. spal = (u32 *)tmspal; // fixed palette in TMS modes
  636. for (i = 0x20/2; i > 0; i--, spal++, dpal++) {
  637. t = *spal;
  638. #if defined(USE_BGR555)
  639. t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<2) | ((t & 0x0f000f00)<<3);
  640. t |= (t >> 4) & 0x04210421;
  641. #elif defined(USE_BGR565)
  642. t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)<<4);
  643. t |= (t >> 4) & 0x08610861;
  644. #else
  645. t = ((t & 0x000f000f)<<12)| ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)>>7);
  646. t |= (t >> 4) & 0x08610861;
  647. #endif
  648. *dpal = t;
  649. }
  650. memcpy(dpal, dpal-0x20/2, 0x20*2); // for prio bit
  651. spal += 0x20/2, dpal += 0x20/2;
  652. }
  653. Pico.est.HighPal[0xe0] = 0;
  654. }
  655. static void FinalizeLineRGB555SMS(int line)
  656. {
  657. if (Pico.m.dirtyPal)
  658. PicoDoHighPal555SMS();
  659. // standard FinalizeLine can finish it for us,
  660. // with features like scaling and such
  661. FinalizeLine555(0, line, &Pico.est);
  662. }
  663. static void FinalizeLine8bitSMS(int line)
  664. {
  665. FinalizeLine8bit(0, line, &Pico.est);
  666. }
  667. void PicoDrawSetOutputSMS(pdso_t which)
  668. {
  669. switch (which)
  670. {
  671. case PDF_8BIT: FinalizeLineSMS = FinalizeLine8bitSMS; break;
  672. case PDF_RGB555: FinalizeLineSMS = FinalizeLineRGB555SMS; break;
  673. // there's no fast renderer yet, just treat it like PDF_8BIT
  674. default: FinalizeLineSMS = FinalizeLine8bitSMS;
  675. PicoDrawSetInternalBuf(Pico.est.Draw2FB, 328); break;
  676. }
  677. rendstatus_old = -1;
  678. mode = -1;
  679. }
  680. // vim:shiftwidth=2:ts=2:expandtab